gpt4 book ai didi

go - 如何获取 UDP 连接的真实本地地址

转载 作者:行者123 更新时间:2023-12-03 10:09:17 24 4
gpt4 key购买 nike

我想开发一个基于 Linux 的 UDP 服务器。该主机上设置了一些 IP(例如 1.1.1.1、1.1.1.2、2001::1:1:1:1),我希望服务器按如下方式监听所有 IP(9090 作为示例)udp6 0 0 :::9090 :::*服务器代码如下

package main

import (
"fmt"
"net"
)

func main() {

udpAddr, err := net.ResolveUDPAddr("udp", ":9090")
conn, err := net.ListenUDP("udp", udpAddr)

if err != nil {
fmt.Println(err)
return
}

var data [1024]byte
n, addr, err := conn.ReadFromUDP(data[:])
if err != nil {
fmt.Println(err)

}
fmt.Println(n)
fmt.Println(addr)
// this is not my wanted result. it will print [::]:9090
fmt.Println(conn.LocalAddr())

}
当客户端调用此服务器时(dst_string为1.1.1.1:9090);
实际结果:
服务器将打印 conn.LocalAddr() 与 [::]:9090 异常结果:
服务器应该打印 1.1.1.1:9090 如何做到这一点?
顺便说一句:我知道 UDP 服务器是否只听 1.1.1.1:9090 可以做到这一点。但是服务器有很多 IP,我希望服务器监听所有 IP 并且 LocalAddr() 可以打印 1.1.1.1:9090

最佳答案

让我们引用 this post来自 PowerDNS 开发人员:

There are two ways to listen on all addresses, one of which is to enumerate all interfaces, grab all their IP addresses, and bind to all of them. Lots of work, and non-portable work too. We really did not want to do that. You also need to monitor new addresses arriving.

Secondly, just bind to 0.0.0.0 and ::! This works very well for TCP and other connection-oriented protocols, but can fail silently for UDP and other connectionless protocols. How come? When a packet comes in on 0.0.0.0, we don’t know which IP address it was sent to. And this is a problem when replying to such a packet – what would the correct source address be? Because we are connectionless (and therefore stateless), the kernel doesn’t know what to do.

So it picks the most appropriate address, and that may be the wrong one. There are some heuristics that make some kernels do the right thing more reliably, but there are no guarantees.

When receiving packets on datagram sockets, we usually use recvfrom(2), but this does not provide the missing bit of data: which IP address the packet was actually sent to. There is no recvfromto(). Enter the very powerful recvmsg(2). Recvmsg() allows for the getting of a boatload of parameters per datagram, as requested via setsockopt().

One of the parameters we can request is the original destination IP address of the packet.
<…>

IPv4

<..> For Linux, use the setsockopt() called IP_PKTINFO, which will get you a parameter over recvmsg() called IP_PKTINFO, which carries a struct in_pktinfo, which has a 4 byte IP address hiding in its ipi_addr field.


看来,最接近 recvmsg() net 中存在的东西包是 net.IPConn.ReadMsgIP ,其文档指出

The packages golang.org/x/net/ipv4 and golang.org/x/net/ipv6 can be used to manipulate IP-level socket options in oob.


因此,看起来像是一条前进的道路。

我还要明确以下几点(从上面的文字中看不出来):
  • 看来,net Go 的 stdlib 包没有标准且易于使用的方式来拥有您想要的东西。
  • 在通配符地址上接收数据报时获取数据报目标地址的方法似乎并没有真正标准化,因此不同内核之间的实现有所不同。
  • 虽然看起来 net.IPConn.ReadMsgIP包裹 recvmsg(2) ,我首先在 Go 标准库的源代码中验证这一点。请注意 stdlib 包含其支持的所有平台的代码这一事实,因此请确保您了解 build constraints 的内容。是。
  • https://godoc.org/golang.org/x/net/可能会有所帮助。 syscall 也是如此包和https://godoc.org/golang.org/x/sys - 如果股票不足。
  • 关于go - 如何获取 UDP 连接的真实本地地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65285074/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com