【发布时间】:2022-09-27 20:40:33
【问题描述】:
我正在学习套接字,我有点困惑为什么 API 与已建立的套接字 API 如此不同(例如在 C 中)。我知道如何在 C 中轻松使用套接字,并希望利用我在 GO 中的一些知识。
具体来说,我想在我的应用程序中使用连接的 UDP 套接字。连接 UDP 套接字的额外好处是将来自已连接客户端的所有传入流量重定向到那个特定的套接字.
如果它有助于理解,这里是我想在 GO 中完成的“流程”(过去曾在 C 和 C# 中完成):
- 客户端发送一个UDP数据包到一个已知的
IP:PORT - 当在此套接字上接收到数据包时,就知道客户端尚未建立 UDP“连接”,因为数据包将到达“已连接”套接字(连接是一个实现细节我的软件)
- 使用同一个端口作为原来的插座。为此,需要 SO_REUSEADDR。
- 来自客户端的所有未来数据包都在新创建的插座。
这种方法的优点是:
- 客户端只需要与一个 IP:PORT 通信
- 无需在每个 UDP 数据包中包含“会话 ID”或类似机制
- 操作系统负责将数据报分派到正确的套接字,从而允许应用程序依赖操作系统。
所以在 GO(过去几天我越来越喜欢的一种语言)中,套接字 API 与我的预期完全不同。我已经能够通过 UDP 侦听 dgram,但是当客户端首次与服务器通信时尝试创建新套接字 (
UDPConn??) 时出现了困难。我的代码:
buf := make([]byte, 32) laddr, _ := net.ResolveUDPAddr(\"udp\", ep.address) var lc = net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { var opErr error if err := c.Control(func(fd uintptr) { opErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) }); err != nil { return err } return opErr }, } conn, err := lc.ListenPacket(context.Background(), \"udp\", ep.address) if err != nil { //TODO log error } for { buf := make([]byte, 32) l, raddr, _ := conn.ReadFrom(buf) laddr, _ := net.ResolveUDPAddr(\"udp\", ep.address) <-- NEED TO SET SO_REUSEADDR BEFORE DIAL/CONNECT --> net.DialUDP(\"udp\", laddr, raddr.(*net.UDPAddr)) fmt.Println(\"Message from: \", raddr.String(), \"Reads: \", string(buf[0:l])) }所以我的想法是
dial等价于connect()。我不确定我是否正确,但无论如何,现在的问题是我在调用dial时无法设置套接字选项。我可能错误地认为dial()将connect(),但我没有看到任何其他方式:- 创建 UDP 套接字
- 绑定它
- 将其连接到远程客户端
运行上述程序时来自
strace的输出:socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 3 setsockopt(3, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0 bind(3, {sa_family=AF_INET, sin_port=htons(6555), sin_addr=inet_addr(\"127.0.0.1\")}, 16) = 0 getsockname(3, {sa_family=AF_INET, sin_port=htons(6555), sin_addr=inet_addr(\"127.0.0.1\")}, [112->16]) = 0 recvfrom(3, 0xc000118040, 32, 0, 0xc00007f6f8, [112]) = -1 EAGAIN (Resource temporarily unavailable) recvfrom(3, \"hi\\n\", 32, 0, {sa_family=AF_INET, sin_port=htons(36682), sin_addr=inet_addr(\"127.0.0.1\")}, [112->16]) = 3 socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 7 setsockopt(7, SOL_SOCKET, SO_BROADCAST, [1], 4) = 0 bind(7, {sa_family=AF_INET, sin_port=htons(6555), sin_addr=inet_addr(\"127.0.0.1\")}, 16) = -1 EADDRINUSE (Address already in use)如您所见,第二个套接字永远不会被创建,因为我必须设置
SO_RESUSEADDR。我的问题: 如何创建一个套接字,设置
SO_REUSEADDR,bind()它和connect()到特定端点。谢谢!
标签: sockets go networking udp