英文:
Multicast doesn't work on Linux in golang
问题
这段代码用于发送和接收组播数据包。
这段代码在Windows 10上可以工作,但在Linux上不能工作:为什么?
数据包被发送(IP 230.0.0.1,目标端口9001),但应用程序无法接收组播数据包(IP 230.0.0.2,目标端口9002)。
问题出在哪里?
为了测试我的应用程序,我使用了一个Linux虚拟机:也许这是原因?
package main
import (
"net"
"os"
"strconv"
"time"
"github.com/rs/zerolog"
"golang.org/x/net/ipv4"
)
const device1_tx_multicastAddr = "230.0.0.1"
const device1_tx_udp_port = 9001
const device2_tx_multicastAddr = "230.0.0.2"
const device2_tx_udp_port = 9002
const packetTxDelayMs = 1000
// const ethName = "Ethernet" // Windows
const ethName = "eth0" // Linux
const modeDevice2 = false // Device 1
//const modeDevice2 = true // Device 2
var logConsole zerolog.Logger
func main() {
logConsole = zerolog.New(os.Stderr).With().Timestamp().
Str("module", "main").
Logger().Output(zerolog.ConsoleWriter{Out: os.Stderr}).
Level(zerolog.InfoLevel)
// **********************************
// Initialize Tx
localInterface := getInterfaceByName(ethName)
logConsole.Info().Str("func", "main").Msg("localInterface: " + ethName)
tx_multicastAddr := device1_tx_multicastAddr
rx_multicastAddr := device2_tx_multicastAddr
tx_udp_port := device1_tx_udp_port
rx_udp_port := device2_tx_udp_port
if modeDevice2 {
tx_multicastAddr = device2_tx_multicastAddr
rx_multicastAddr = device1_tx_multicastAddr
tx_udp_port = device2_tx_udp_port
rx_udp_port = device1_tx_udp_port
}
logConsole.Info().Str("func", "main").Msg("Open Tx UDP port " + tx_multicastAddr + ":" + strconv.Itoa(tx_udp_port) + "...")
remoteDeviceUdpAddr, err := net.ResolveUDPAddr("udp4", tx_multicastAddr+":"+strconv.Itoa(tx_udp_port))
if err != nil {
panic(err)
}
localDeviceUdpAddr, err2 := net.ResolveUDPAddr("udp4", localInterface.String()+":"+strconv.Itoa(rx_udp_port))
if err2 != nil {
panic(err2)
}
logConsole.Info().Str("func", "main").Msg("Listen UDP: " + localDeviceUdpAddr.String() + "...")
localDevice, err2 := net.ListenUDP("udp4", localDeviceUdpAddr)
if err2 != nil {
panic(err2)
}
// **********************************
// Initialize Rx
udpReceiver := ipv4.NewPacketConn(localDevice)
ief, errInterface := net.InterfaceByName(ethName)
if errInterface != nil {
localDevice.Close()
panic(errInterface)
}
logConsole.Info().Str("func", "main").Msg("Join Multicast: " + rx_multicastAddr + "...")
err = udpReceiver.JoinGroup(ief, &net.UDPAddr{IP: net.ParseIP(rx_multicastAddr)})
if err != nil {
localDevice.Close()
panic(err)
}
// **********************************
// Run Rx/Tx tasks
go sendData(localDevice, remoteDeviceUdpAddr, packetTxDelayMs)
receivedData(udpReceiver)
}
// *************************************************
func sendData(localDevice *net.UDPConn, remoteDeviceUdpAddr *net.UDPAddr, packetDelay uint) {
data := []byte("1234567890")
for {
//logConsole.Info().Str("func", "sendData").Msg("Send...")
_, err := localDevice.WriteTo(data, remoteDeviceUdpAddr)
if err != nil {
panic(err)
}
time.Sleep(time.Duration(packetDelay) * time.Millisecond)
}
}
func receivedData(receiver *ipv4.PacketConn) {
buf := make([]byte, 1500)
for {
n, _, _, err := receiver.ReadFrom(buf)
if err == nil {
logConsole.Info().Str("func", "receivedData").Msg("Receive Data: " + string(buf[0:n]))
}
}
}
// *************************************************
func getInterfaceByName(name string) net.IP {
ief, err := net.InterfaceByName(name)
if err != nil {
panic(err)
}
addrs, err := ief.Addrs()
if err != nil {
panic(err)
}
var ipAddr net.IP
for _, addr := range addrs {
ipAddr = addr.(*net.IPNet).IP.To4()
if ipAddr != nil {
break
}
}
if ipAddr == nil {
panic("ipAddr is nil")
}
return ipAddr
}
英文:
This code sends and receives multicast paquets.
This code works on Windows 10 but not with Linux : why ?
Packed are sent (IP 230.0.0.1, dest port 9001) but application doesn't receive multicast
packet (IP 230.0.0.2, dest port 9002).
What is the problem ?
To test my application, I use a Linux VM : perahaps, it is the cause ?
package main
import (
"net"
"os"
"strconv"
"time"
"github.com/rs/zerolog"
"golang.org/x/net/ipv4"
)
const device1_tx_multicastAddr = "230.0.0.1"
const device1_tx_udp_port = 9001
const device2_tx_multicastAddr = "230.0.0.2"
const device2_tx_udp_port = 9002
const packetTxDelayMs = 1000
// const ethName = "Ethernet" // Windows
const ethName = "eth0" // Linux
const modeDevice2 = false // Device 1
//const modeDevice2 = true // Device 2
var logConsole zerolog.Logger
func main() {
logConsole = zerolog.New(os.Stderr).With().Timestamp().
Str("module", "main").
Logger().Output(zerolog.ConsoleWriter{Out: os.Stderr}).
Level(zerolog.InfoLevel)
// **********************************
// Initialize Tx
localInterface := getInterfaceByName(ethName)
logConsole.Info().Str("func", "main").Msg("localInterface: " + ethName)
tx_multicastAddr := device1_tx_multicastAddr
rx_multicastAddr := device2_tx_multicastAddr
tx_udp_port := device1_tx_udp_port
rx_udp_port := device2_tx_udp_port
if modeDevice2 {
tx_multicastAddr = device2_tx_multicastAddr
rx_multicastAddr = device1_tx_multicastAddr
tx_udp_port = device2_tx_udp_port
rx_udp_port = device1_tx_udp_port
}
logConsole.Info().Str("func", "main").Msg("Open Tx UDP port " + tx_multicastAddr + ":" + strconv.Itoa(tx_udp_port) + "...")
remoteDeviceUdpAddr, err := net.ResolveUDPAddr("udp4", tx_multicastAddr+":"+strconv.Itoa(tx_udp_port))
if err != nil {
panic(err)
}
localDeviceUdpAddr, err2 := net.ResolveUDPAddr("udp4", localInterface.String()+":"+strconv.Itoa(rx_udp_port))
if err2 != nil {
panic(err2)
}
logConsole.Info().Str("func", "main").Msg("Listen UDP: " + localDeviceUdpAddr.String() + "...")
localDevice, err2 := net.ListenUDP("udp4", localDeviceUdpAddr)
if err2 != nil {
panic(err2)
}
// **********************************
// Initialize Rx
udpReceiver := ipv4.NewPacketConn(localDevice)
ief, errInterface := net.InterfaceByName(ethName)
if errInterface != nil {
localDevice.Close()
panic(errInterface)
}
logConsole.Info().Str("func", "main").Msg("Join Multicast: " + rx_multicastAddr + "...")
err = udpReceiver.JoinGroup(ief, &net.UDPAddr{IP: net.ParseIP(rx_multicastAddr)})
if err != nil {
localDevice.Close()
panic(err)
}
// **********************************
// Run Rx/Tx tasks
go sendData(localDevice, remoteDeviceUdpAddr, packetTxDelayMs)
receivedData(udpReceiver)
}
// *************************************************
func sendData(localDevice *net.UDPConn, remoteDeviceUdpAddr *net.UDPAddr, packetDelay uint) {
data := []byte("1234567890")
for {
//logConsole.Info().Str("func", "sendData").Msg("Send...")
_, err := localDevice.WriteTo(data, remoteDeviceUdpAddr)
if err != nil {
panic(err)
}
time.Sleep(time.Duration(packetDelay) * time.Millisecond)
}
}
func receivedData(receiver *ipv4.PacketConn) {
buf := make([]byte, 1500)
for {
n, _, _, err := receiver.ReadFrom(buf)
if err == nil {
logConsole.Info().Str("func", "receivedData").Msg("Receive Data: " + string(buf[0:n]))
}
}
}
// *************************************************
func getInterfaceByName(name string) net.IP {
ief, err := net.InterfaceByName(name)
if err != nil {
panic(err)
}
addrs, err := ief.Addrs()
if err != nil {
panic(err)
}
var ipAddr net.IP
for _, addr := range addrs {
ipAddr = addr.(*net.IPNet).IP.To4()
if ipAddr != nil {
break
}
}
if ipAddr == nil {
panic("ipAddr is nil")
}
return ipAddr
}
答案1
得分: 1
修改应用程序以侦听以下IP地址之一将使其在Linux和macOS上运行:
- 多播组的IP地址(问题中的
rx_multicastAddr
) - 通配符地址(
0.0.0.0
)
但是,如果它侦听网络接口卡的IP地址(例如192.168.0.5
),目前还不清楚它是否能正常工作。根据我的测试和问题描述,它在Windows上工作正常,但在Linux或macOS上不起作用。我目前找不到一个权威的来源来描述这种行为。
下面是一个简化的演示,接受标志。
在设备1上,使用以下命令运行它(将接口名称替换为您的设备的名称):
go run . -listen 230.0.0.1:9001 -join 230.0.0.1:9001 -send 230.0.0.2:9002 -ifname eth0
在设备2上,使用以下命令运行它(将接口名称替换为您的设备的名称):
go run . -listen 0.0.0.0:9002 -join 230.0.0.2:9002 -send 230.0.0.1:9001 -ifname Ethernet
package main
import (
"flag"
"log"
"net"
"time"
"golang.org/x/net/ipv4"
)
var (
listen string
join string
send string
ifname string
)
func main() {
flag.StringVar(&listen, "listen", "230.0.0.1:9001", "")
flag.StringVar(&join, "join", "230.0.0.1:9001", "the multicast group address to receive data from")
flag.StringVar(&send, "send", "230.0.0.2:9002", "the multicast group address to send data to")
flag.StringVar(&ifname, "ifname", "eth0", "the name of the interface")
flag.Parse()
itf, err := net.InterfaceByName(ifname)
if err != nil {
panic(err)
}
groupAddr, err := net.ResolveUDPAddr("udp4", join)
if err != nil {
panic(err)
}
c, err := net.ListenPacket("udp4", listen)
if err != nil {
panic(err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
if err := p.JoinGroup(itf, &net.UDPAddr{IP: groupAddr.IP}); err != nil {
panic(err)
}
log.Printf("join multicast group %s, waiting...", join)
go sendData(c, send)
receivedData(p)
}
func sendData(c net.PacketConn, target string) {
data := []byte(ifname)
addr, err := net.ResolveUDPAddr("udp4", target)
if err != nil {
panic(err)
}
for {
_, err := c.WriteTo(data, addr)
if err != nil {
panic(err)
}
time.Sleep(time.Second)
}
}
func receivedData(receiver *ipv4.PacketConn) {
buf := make([]byte, 1500)
for {
n, _, _, err := receiver.ReadFrom(buf)
if err == nil {
log.Printf("Receive Data from: %s\n", buf[0:n])
}
}
}
希望对你有所帮助!
英文:
Modifying the application to listen on one of the following IP addresses will make it work on Linux and macOS:
- the IP address of the multicast group (
rx_multicastAddr
in the question) - the wildcard address (
0.0.0.0
).
But it's not clear should it work when it listens on an IP address of a NIC (for example, 192.168.0.5
). According to my test and the description in the question, it works on Windows but does not work on Linux or macOS. I can not find an authoritative source that describes this behavior yet.
Below is a simplified demo that accepts flags.
On device 1, run it with this command (replace the interface name with the one for your device):
go run . -listen 230.0.0.1:9001 -join 230.0.0.1:9001 -send 230.0.0.2:9002 -ifname eth0
On device 2, run it with this command (replace the interface name with the one for your device):
go run . -listen 0.0.0.0:9002 -join 230.0.0.2:9002 -send 230.0.0.1:9001 -ifname Ethernet
package main
import (
"flag"
"log"
"net"
"time"
"golang.org/x/net/ipv4"
)
var (
listen string
join string
send string
ifname string
)
func main() {
flag.StringVar(&listen, "listen", "230.0.0.1:9001", "")
flag.StringVar(&join, "join", "230.0.0.1:9001", "the multicast group address to receive data from")
flag.StringVar(&send, "send", "230.0.0.2:9002", "the multicast group address to send data to")
flag.StringVar(&ifname, "ifname", "eth0", "the name of the interface")
flag.Parse()
itf, err := net.InterfaceByName(ifname)
if err != nil {
panic(err)
}
groupAddr, err := net.ResolveUDPAddr("udp4", join)
if err != nil {
panic(err)
}
c, err := net.ListenPacket("udp4", listen)
if err != nil {
panic(err)
}
defer c.Close()
p := ipv4.NewPacketConn(c)
if err := p.JoinGroup(itf, &net.UDPAddr{IP: groupAddr.IP}); err != nil {
panic(err)
}
log.Printf("join multicast group %s, waiting...", join)
go sendData(c, send)
receivedData(p)
}
func sendData(c net.PacketConn, target string) {
data := []byte(ifname)
addr, err := net.ResolveUDPAddr("udp4", target)
if err != nil {
panic(err)
}
for {
_, err := c.WriteTo(data, addr)
if err != nil {
panic(err)
}
time.Sleep(time.Second)
}
}
func receivedData(receiver *ipv4.PacketConn) {
buf := make([]byte, 1500)
for {
n, _, _, err := receiver.ReadFrom(buf)
if err == nil {
log.Printf("Receive Data from: %s\n", buf[0:n])
}
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论