【问题标题】:In Golang, how to receive multicast packets with socket bound to specific address/port?在 Golang 中,如何接收绑定到特定地址/端口的套接字的多播数据包?
【发布时间】:2016-05-19 21:50:07
【问题描述】:

手头的任务是将一个套接字专门绑定到地址 1.0.0.2:520(分配给 eth2),然后读取地址为 224.0.0.9:520 的多播 UDP 数据包。

我正在尝试下面的代码,基于https://godoc.org/golang.org/x/net/ipv4

不幸的是,这个调试消息永远不会到达:log.Printf("udpReader: recv %d bytes from %s to %s on %s", n, cm.Src, cm.Dst, ifname)

我知道 eth2 正在接收所需的数据包,因为我在上面运行了这个数据包嗅探器:

sudo tcpdump -n -i eth2
18:40:28.571456 IP 1.0.0.1.520 > 224.0.0.9.520: RIPv2, Request, length: 24
18:40:29.556503 IP 1.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 64

这是示例代码。你能看出为什么它不起作用吗?

package main

import (
    "fmt"
    "log"
    "net"

    "golang.org/x/net/ipv4"
)

func main() {
    if err := interfaceAdd("eth2"); err != nil {
        log.Printf("main: error: %v", err)
    }

    log.Printf("main: waiting forever")
    <-make(chan int)
}

func interfaceAdd(s string) error {

    iface, err1 := net.InterfaceByName(s)
    if err1 != nil {
        return err1
    }

    addrList, err2 := iface.Addrs()
    if err2 != nil {
        return err2
    }

    for _, a := range addrList {
        addr, _, err3 := net.ParseCIDR(a.String())
        if err3 != nil {
            log.Printf("interfaceAdd: parse CIDR error for '%s' on '%s': %v", addr, s, err3)
            continue
        }
        if err := join(iface, addr); err != nil {
            log.Printf("interfaceAdd: join error for '%s' on '%s': %v", addr, s, err)
        }
    }

    return nil
}

func join(iface *net.Interface, addr net.IP) error {
    proto := "udp"
    var a string
    if addr.To4() == nil {
        // IPv6
        a = fmt.Sprintf("[%s]", addr.String())
    } else {
        // IPv4
        a = addr.String()
    }

    hostPort := fmt.Sprintf("%s:520", a) // rip multicast port

    // open socket (connection)
    conn, err2 := net.ListenPacket(proto, hostPort)
    if err2 != nil {
        return fmt.Errorf("join: %s/%s listen error: %v", proto, hostPort, err2)
    }

    // join multicast address
    pc := ipv4.NewPacketConn(conn)
    if err := pc.JoinGroup(iface, &net.UDPAddr{IP: net.IPv4(224, 0, 0, 9)}); err != nil {
        conn.Close()
        return fmt.Errorf("join: join error: %v", err)
    }

    // request control messages
    /*
        if err := pc.SetControlMessage(ipv4.FlagTTL|ipv4.FlagSrc|ipv4.FlagDst|ipv4.FlagInterface, true); err != nil {
            // warning only
            log.Printf("join: control message flags error: %v", err)
        }
    */

    go udpReader(pc, iface.Name, addr.String())

    return nil
}

func udpReader(c *ipv4.PacketConn, ifname, ifaddr string) {

    log.Printf("udpReader: reading from '%s' on '%s'", ifaddr, ifname)

    defer c.Close()

    buf := make([]byte, 10000)

    for {
        n, cm, _, err := c.ReadFrom(buf)
        if err != nil {
            log.Printf("udpReader: ReadFrom: error %v", err)
            break
        }

        // make a copy because we will overwrite buf
        b := make([]byte, n)
        copy(b, buf)

        log.Printf("udpReader: recv %d bytes from %s to %s on %s", n, cm.Src, cm.Dst, ifname)
    }

    log.Printf("udpReader: exiting '%s'", ifname)
}

输出:

2016/02/09 18:44:20 interfaceAdd: join error for 'fe80::a00:27ff:fe52:9575' on 'eth2': join: udp/[fe80::a00:27ff:fe52:9575]:520 listen error: listen udp [fe80::a00:27ff:fe52:9575]:520: bind: invalid argument
2016/02/09 18:44:20 main: waiting forever
2016/02/09 18:44:20 udpReader: reading from '1.0.0.2' on 'eth2'

【问题讨论】:

  • 只是为了明确一点:任务不是绑定到侦听该 IP 的接口,而是具体且积极地绑定到 IP 本身?
  • 任务是接收地址为 224.0.0.9 的数据包,但仅限于特定接口。绑定到 IP 地址是我想我应该用来实现该目标的机制。
  • 不。通过 IP 识别要绑定的接口,然后绑定到该接口并加入多播组。
  • 好吧,我认为这就是代码所做的:ListenPacket() 绑定到接口的 IP 地址,然后 JoinGroup() 加入多播组。但是,它不起作用。你能告诉错误在哪里吗?
  • 发送方是否启用了 IP_MULTICAST_LOOP?

标签: sockets go ip multicast


【解决方案1】:

为了创建多个特定于接口的套接字来接收寻址到相同多播地址 (224.0.0.9:520) 的数据包,我原来的 Go 代码缺少三个主要问题:

  1. 要将多个套接字绑定到同一个 UDP 端口,请设置 syscall.SO_REUSEADDR
  2. 为了将套接字限制到特定接口,请设置 syscall.SO_BINDTODEVICE
  3. 将 UDP 套接字绑定到 0.0.0.0:520

在这里找到完整的示例代码:http://play.golang.org/p/NprsZPHQmj

【讨论】:

    猜你喜欢
    • 2021-04-16
    • 2011-08-31
    • 2019-06-02
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-12
    • 2012-08-29
    相关资源
    最近更新 更多