多播在Linux上在golang中无法工作。

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

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])
		}
	}
}

huangapple
  • 本文由 发表于 2023年6月2日 22:27:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76391050.html
匿名

发表评论

匿名网友

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

确定