>  기사  >  백엔드 개발  >  멀티캐스트가 golang의 Linux에서 작동하지 않습니다.

멀티캐스트가 golang의 Linux에서 작동하지 않습니다.

WBOY
WBOY앞으로
2024-02-10 16:42:07993검색

多播在 golang 中的 Linux 上不起作用

PHP 편집자 Xinyi님, 오늘 저는 golang의 멀티캐스트가 Linux에서 작동하지 않는 문제에 대해 논의하고 싶습니다. 멀티캐스트는 단일 송신자와 다수의 수신자 간에 데이터를 전송하는 네트워크 통신 방법입니다. 그러나 golang에서는 Linux 운영 체제에서 멀티캐스트가 작동하지 않는 상황이 발생할 수 있습니다. 이 문서에서는 이 문제가 발생하는 이유를 설명하고 가능한 해결 방법을 제공합니다. 시작하자!

질문 내용

이 코드는 멀티캐스트 패킷을 보내고 받습니다.

이 코드는 Windows 10에서는 작동하지만 Linux에서는 작동하지 않습니다. 왜 그럴까요?

패키지 콘텐츠(ip 230.0.0.1, 대상 포트 9001)를 보냈으나 애플리케이션에서 멀티캐스트를 수신하지 못했습니다

패킷(ip 230.0.0.2, 대상 포트 9002).

무엇이 문제인가요?

내 애플리케이션을 테스트하기 위해 Linux VM을 사용했습니다. 아마도 그게 이유일까요?

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
}

해결 방법

다음 IP 주소 중 하나를 수신하도록 애플리케이션을 수정하면 Linux 및 Macos에서 실행됩니다.

  • 멀티캐스트 그룹의 IP 주소(rx_multicastaddr 질문에)
  • 와일드카드 주소(0.0.0.0).

그러나 NIC의 IP 주소(예: 192.168.0.5)를 수신할 때 작동할지는 확실하지 않습니다. 내 테스트와 질문의 설명에 따르면 Windows에서는 작동하지만 Linux 또는 Macos에서는 작동하지 않습니다. 이 동작을 설명하는 권위 있는 소스를 찾을 수 없습니다.

아래는 수락 플래그에 대한 간단한 데모입니다.

장치 1에서 다음 명령을 사용하여 실행합니다(인터페이스 이름을 장치 이름으로 바꿉니다):

으아악

장치 2에서 다음 명령을 사용하여 실행합니다(인터페이스 이름을 장치의 인터페이스 이름으로 바꿉니다):

으아악 으아악

위 내용은 멀티캐스트가 golang의 Linux에서 작동하지 않습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 stackoverflow.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제