Maison > développement back-end > Golang > le corps du texte

La multidiffusion ne fonctionne pas sous Linux dans Golang

WBOY
Libérer: 2024-02-10 16:42:07
avant
993 Les gens l'ont consulté

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

Éditeur PHP Xinyi, aujourd'hui je veux discuter avec vous du problème que la multidiffusion dans Golang ne fonctionne pas sous Linux. La multidiffusion est une méthode de communication réseau qui transmet des données entre un seul expéditeur et plusieurs récepteurs. Cependant, dans Golang, nous pouvons rencontrer des situations où la multidiffusion ne fonctionne pas sur les systèmes d'exploitation Linux. Cet article explique pourquoi ce problème se produit et propose des solutions possibles. commençons!

Contenu de la question

Ce code envoie et reçoit des paquets multicast.

Ce code fonctionne sous Windows 10 mais pas sous Linux : pourquoi ?

Le contenu packagé (ip 230.0.0.1, port de destination 9001) a été envoyé, mais la multidiffusion n'a pas été reçue par l'application

Packet (ip 230.0.0.2, port de destination 9002).

Quel est le problème ?

Pour tester mon application j'ai utilisé une vm Linux : peut-être, c'est la raison ?

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
}
Copier après la connexion

Solution de contournement

Modifier l'application pour qu'elle écoute sur l'une des adresses IP suivantes la fera fonctionner sous Linux et Macos :

  • L'adresse IP du groupe multicast (rx_multicastaddr dans la question)
  • Adresse générique (0.0.0.0).

Mais il n'est pas clair si cela fonctionnera lorsqu'il écoutera l'adresse IP d'un nic (par exemple 192.168.0.5). D'après mes tests et la description de la question, cela fonctionne sous Windows, mais pas sous Linux ou Macos. Je n'ai pas pu trouver de source faisant autorité décrivant ce comportement.

Vous trouverez ci-dessous une démonstration simplifiée du drapeau d'acceptation.

Sur l'appareil 1, exécutez-le avec la commande suivante (remplacez le nom de l'interface par le nom de votre appareil) :

go run . -listen 230.0.0.1:9001 -join 230.0.0.1:9001 -send 230.0.0.2:9002 -ifname eth0
Copier après la connexion

Sur l'appareil 2, exécutez-le avec la commande suivante (remplacez le nom de l'interface par le nom de l'interface de votre appareil) :

go run . -listen 0.0.0.0:9002 -join 230.0.0.2:9002 -send 230.0.0.1:9001 -ifname ethernet
Copier après la connexion
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])
        }
    }
}
Copier après la connexion

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:stackoverflow.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!