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

Go 如何获取UDP连接的真实本地地址,go,Go,我想要一个基于linux的udp服务器。该主机上设置了一些IP(例如1.1.1.1,1.1.1.22001::1:1:1:1),我希望服务器在所有IP上侦听,如下所示(示例为9090) udp6 0:::9090:::* 服务器代码如下所示 package main import ( "fmt" "net" ) func main() { udpAddr, err := net.ResolveUDPAddr("udp

我想要一个基于linux的udp服务器。该主机上设置了一些IP(例如1.1.1.1,1.1.1.22001::1:1:1:1),我希望服务器在所有IP上侦听,如下所示(示例为9090)

udp6 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())

}
root@test-VirtualBox:/home/test/mygo/src/udp# go run s2.go
{[64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 0}
[64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
22:20:47.009712 IP6 ::1.43305 > ::1.1025: UDP, length 6
当客户端拨号到此服务器时(dst_字符串为1.1.1.1:9090)

实际结果: 服务器将使用

[:]:9090

例外结果:

服务器应该打印

1.1.1.1:9090

如何做到这一点

顺便说一句:我知道udp服务器是否只监听1.1.1.1:9090就能做到这一点。但是服务器有很多ip,我希望服务器侦听所有ip和LocalAddr()可以打印1.1.1.1:9090

监听所有地址有两种方法,其中一种是枚举所有接口,获取它们的所有IP地址,并绑定到所有接口。大量的工作,以及不可移动的工作。我们真的不想那样做。您还需要监视到达的新地址

其次,只需绑定到
0.0.0.0
!这对于TCP和其他面向连接的协议非常有效,但对于UDP和其他无连接的协议可能会以静默方式失败。怎么会?当数据包在
0.0.0.0
上传入时,我们不知道它发送到哪个IP地址。在回复这样的数据包时,这是一个问题——正确的源地址是什么?因为我们是无连接的(因此是无状态的),内核不知道该做什么

因此,它选择了最合适的地址,而这可能是错误的地址。有一些启发式方法可以使一些内核更可靠地做正确的事情,但没有保证

在数据报套接字上接收数据包时,我们通常使用
recvfrom(2)
,但这并不提供丢失的数据位:数据包实际发送到哪个IP地址。没有
recvfromto()
。输入非常强大的
recvmsg(2)
Recvmsg()
允许根据
setsockopt()
的请求,获取每个数据报的大量参数

我们可以请求的参数之一是数据包的原始目标IP地址。

IPv4 对于Linux,使用名为
IP\u PKTINFO
setsockopt()
,它将在名为
IP\u PKTINFO
recvmsg()上获得一个参数,该参数在
ipi\u addr
字段中隐藏一个4字节的IP地址

看起来,
net
包中最接近
recvmsg()
的东西是,它的文档说明

这些包和可用于操作
oob
中的IP级别套接字选项

因此,这似乎是一条前进的道路


我还要明确以下几点(从上面的文本中看不明显):

  • 看来,Go的stdlib的
    net
    软件包并没有一种标准且易于使用的方式来满足您的需求
  • 当在通配符地址上接收数据报时,获取数据报目标地址的方法似乎并没有真正标准化,因此不同内核的实现不同
  • 虽然看起来
    net.IPConn.ReadMsgIP
    包装了
    recvmsg(2)
    ,但我首先要在Go标准库的源代码中验证这一点。请注意,stdlib包含它所支持的所有平台的代码,因此请确保您了解它们是什么
  • 也许会有帮助。
    syscall
    包也是如此,如果库存不足

    • 非常感谢kostix的回复。 根据IP_PKTINFO提示,我发现下面的代码可以直接解决我的ipv4问题

      但是对于ipv6,结果仍然例外

      package main
      
      import (
          "bytes"
          "encoding/binary"
          "fmt"
          "net"
          "syscall"
      )
      
      func main() {
          serverAddr, _ := net.ResolveUDPAddr("udp", ":9999")
          sConn, _ := net.ListenUDP("udp", serverAddr)
          file, _ := sConn.File()
      
          syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IP_PKTINFO, 1)
      
          data := make([]byte, 1024)
          oob := make([]byte, 2048)
      
          sConn.ReadMsgUDP(data, oob)
      
          oob_buffer := bytes.NewBuffer(oob)
          msg := syscall.Cmsghdr{}
          binary.Read(oob_buffer, binary.LittleEndian, &msg)
      
          if msg.Level == syscall.IPPROTO_IPV6 && msg.Type == syscall.IP_PKTINFO {
              packet_info := syscall.Inet6Pktinfo{}
              binary.Read(oob_buffer, binary.LittleEndian, &packet_info)
              fmt.Println(packet_info)
              // the ipv6 address is not my wanted result
              fmt.Println(packet_info.Addr)
          }
      
      }
      
      结果如下

      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())
      
      }
      
      root@test-VirtualBox:/home/test/mygo/src/udp# go run s2.go
      {[64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 0}
      [64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
      
      22:20:47.009712 IP6 ::1.43305 > ::1.1025: UDP, length 6
      
      tcpdump嗅探器如下所示

      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())
      
      }
      
      root@test-VirtualBox:/home/test/mygo/src/udp# go run s2.go
      {[64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 0}
      [64 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
      
      22:20:47.009712 IP6 ::1.43305 > ::1.1025: UDP, length 6
      

      我通过设置ipv6选项来解决此ipv6问题
      err=syscall.SetsockoptInt(int(fd),syscall.IPPROTO\u IPV6,syscall.IPV6\u RECVPKTINFO,1)


      谢谢大家

      你的意思是?:
      RemoteAddr()
      拼写错误。它是LocalAddr()