ホームページ >バックエンド開発 >Golang >Linuxのgolangでマルチキャストが機能しない

Linuxのgolangでマルチキャストが機能しない

WBOY
WBOY転載
2024-02-10 16:42:071062ブラウズ

多播在 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では動作しません。この動作を説明している信頼できる情報源を見つけることができませんでした。

以下は、accept フラグの簡略化されたデモンストレーションです。

デバイス 1 で、次のコマンドを使用して実行します (インターフェイス名をデバイスの名前に置き換えます):

リーリー

デバイス 2 で、次のコマンドを使用して実行します (インターフェイス名をデバイスのインターフェイス名に置き換えます):

リーリー リーリー

以上がLinuxのgolangでマルチキャストが機能しないの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はstackoverflow.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。