如何在Golang中设置多播UDPConn的IP_MULTICAST_LOOP选项

huangapple go评论109阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2017年3月30日 13:52:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/43109552.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定