英文:
How to set IP_MULTICAST_LOOP on multicast UDPConn in Golang
问题
我需要在多播UDP连接/套接字上设置IP_MULTICAST_LOOP,以便在本地机器上发送/接收多播数据包。我找到了应该起作用的调用代码:
l, err := net.ListenMulticastUDP("udp4", nil, addr)
file, err := l.File()
fd := syscall.Handle(file.Fd())
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 1)
然而,它失败并显示“不受Windows支持”。我相当确定Windows的套接字支持此功能,只是Go的net包不支持。你有什么办法可以在我的连接上设置这个标志吗?(我对Go语言还不熟悉,可能忽略了一些显而易见的东西)。这是在Windows上,我还没有机会在Linux上测试。
英文:
I need to set IP_MULTICAST_LOOP on a multicast UDP connection/socket so I can send/receive multicast packets on my local machine. This is the call that I've found which should work:
l, err := net.ListenMulticastUDP("udp4", nil, addr)
file, err := l.File()
fd := syscall.Handle(file.Fd())
err = syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, 1)
However it fails with "not supported by windows". I'm pretty sure this is supported by sockets in windows, just not in the Go net package. Any idea on how I can set this flag on my connection? (I'm new to the Go language, I may have overlooked something obvious). This is on Windows, I haven't had a chance to test on Linux.
答案1
得分: 2
根据文档所说,net.ListenMulticastUDP
只适用于简单小型应用程序的方便性。对于通用用途,你可以使用golang.org/x/net/ipv4,该包提供了更多关于多播的选项。实际上,net.ListenMulticastUDP()
的源代码将IP_MULTICAST_LOOP
设置为false:
func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
if ifi != nil {
if err := setIPv4MulticastInterface(c.fd, ifi); err != nil {
return err
}
}
if err := setIPv4MulticastLoopback(c.fd, false); err != nil {
return err
}
if err := joinIPv4Group(c.fd, ifi, ip); err != nil {
return err
}
return nil
}
setIPv4MulticastLoopback()
在不同的操作系统上实现,并且它没有被导出。对于Windows,它在sockoptip_windows.go
中:
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)))
}
下面是一个使用golang.org/x/net/ipv4
的示例,你可以获取/设置MulticastLoopback
:
package main
import (
"fmt"
"net"
"golang.org/x/net/ipv4"
)
func main() {
ipv4Addr := &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251), Port: 5352}
conn, err := net.ListenUDP("udp4", ipv4Addr)
if err != nil {
fmt.Printf("ListenUDP error %v\n", err)
return
}
pc := ipv4.NewPacketConn(conn)
// 假设你有一个名为wlan的接口
iface, err := net.InterfaceByName("wlan")
if err != nil {
fmt.Printf("can't find specified interface %v\n", err)
return
}
if err := pc.JoinGroup(iface, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251)}); err != nil {
return
}
// 测试
if loop, err := pc.MulticastLoopback(); err == nil {
fmt.Printf("MulticastLoopback status:%v\n", loop)
if !loop {
if err := pc.SetMulticastLoopback(true); err != nil {
fmt.Printf("SetMulticastLoopback error:%v\n", err)
}
}
}
if _, err := conn.WriteTo([]byte("hello"), ipv4Addr); err != nil {
fmt.Printf("Write failed, %v\n", err)
}
buf := make([]byte, 1024)
for {
if n, addr, err := conn.ReadFrom(buf); err != nil {
fmt.Printf("error %v", err)
} else {
fmt.Printf("recv %s from %v\n", string(buf[:n]), addr)
}
}
return
}
英文:
As the doc says, net.ListenMulticastUDP
is just for convenience of simple small applications. You can use golang.org/x/net/ipv4 for general purpose uses, this package offers you more options about multicast.. Actually, the source code of net.ListenMulticastUDP()
sets IP_MULTICAST_LOOP
to false:
func listenIPv4MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
if ifi != nil {
if err := setIPv4MulticastInterface(c.fd, ifi); err != nil {
return err
}
}
if err := setIPv4MulticastLoopback(c.fd, false); err != nil {
return err
}
if err := joinIPv4Group(c.fd, ifi, ip); err != nil {
return err
}
return nil
}
setIPv4MulticastLoopback()
is implemented for different OSes, and it's not exported. For Windows, it's in sockoptip_windows.go
:
func setIPv4MulticastLoopback(fd *netFD, v bool) error {
if err := fd.incref(); err != nil {
return err
}
defer fd.decref()
return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_IP, syscall.IP_MULTICAST_LOOP, boolint(v)))
}
Below is an example with golang.org/x/net/ipv4
, you can get/set MulticastLoopback
package main
import (
"fmt"
"net"
"golang.org/x/net/ipv4"
)
func main() {
ipv4Addr := &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251), Port: 5352}
conn, err := net.ListenUDP("udp4", ipv4Addr)
if err != nil {
fmt.Printf("ListenUDP error %v\n", err)
return
}
pc := ipv4.NewPacketConn(conn)
// assume your have a interface named wlan
iface, err := net.InterfaceByName("wlan")
if err != nil {
fmt.Printf("can't find specified interface %v\n", err)
return
}
if err := pc.JoinGroup(iface, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 251)}); err != nil {
return
}
// test
if loop, err := pc.MulticastLoopback(); err == nil {
fmt.Printf("MulticastLoopback status:%v\n", loop)
if !loop {
if err := pc.SetMulticastLoopback(true); err != nil {
fmt.Printf("SetMulticastLoopback error:%v\n", err)
}
}
}
if _, err := conn.WriteTo([]byte("hello"), ipv4Addr); err != nil {
fmt.Printf("Write failed, %v\n", err)
}
buf := make([]byte, 1024)
for {
if n, addr, err := conn.ReadFrom(buf); err != nil {
fmt.Printf("error %v", err)
} else {
fmt.Printf("recv %s from %v\n", string(buf[:n]), addr)
}
}
return
}
答案2
得分: 0
我认为stdlib ListenMulticastUDP默认禁用回环功能有点愚蠢,因为这使得它在测试中完全无用(例如,在测试机器上运行多个节点),而且文档上并没有明确说明它特别禁用了回环功能,这是不合理和令人烦恼的。
https://github.com/p9c/pod/blob/master/pkg/comm/multicast/channel.go 是一个简单的函数,它返回一个启用回环功能的标准224.0.0.1地址和任意端口。我想如果它能让你选择回环地址会更好,但对于构建局域网组播IPv4系统,这已经足够了。
英文:
I think it's kinda stupid that the stdlib ListenMulticastUDP disables loopback by default because it renders it completely useless for testing purposes (ie, running more than one node on a test machine) and it doesn't say clearly on the documentation that it specifically disables loopback, it's unjustified and annoying.
https://github.com/p9c/pod/blob/master/pkg/comm/multicast/channel.go is a simple function that returns a standard 224.0.0.1 with an arbitrary port with loopback enabled. I suppose it would be even nicer if it let you pick the loopback address but for building LAN multicast IPv4 systems this is all you need.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论