【问题标题】:C - choose interface for UDP/multicast socketC - 为 UDP/组播套接字选择接口
【发布时间】:2012-09-22 18:20:03
【问题描述】:

我正在尝试修改 multicast listener / sender example 以将 UDP / 多播套接字绑定到特定接口,使用 INADDR_ANY 宏。

我拥有接口的 IPv4 地址。 我尝试了以下方法,但套接字没有收到任何 UDP(单播、广播、多播)数据包。

struct sockaddr_in addr;
int fd, nbytes;
socklen_t  addrlen;
struct ip_mreq mreq;

// my_ipv4Addr equals current IP as String, e.g. "89.89.89.89"

// create what looks like an ordinary UDP socket */
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
    perror("socket");
    exit(1);
}

// set up addresses
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
// [-]    addr.sin_addr.s_addr = htonl(INADDR_ANY); 
addr.sin_addr.s_addr = inet_addr(my_ipv4Addr); 
addr.sin_port = htons(port);

// bind socket
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    perror("bind");
    exit(1);
}

// use setsockopt() to request that the kernel join a multicast group
mreq.imr_multiaddr.s_addr = inet_addr(group);
// [-]    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);
if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))< 0) {
    perror("setsockopt");
    exit(1);
}

编辑:

让我解释一下我的程序的目的。 我正在编写一个小工具,它将检查网络是否支持广播/多播。 因此,我拥有一个具有两个接口的系统,并通过 Interface1 发送一个多播数据包并尝试使用 Interface2 接收它。但是:数据包应该通过网络,而不是环包设备。

这个想法是在 thread1/interface1 上阻止多播环回:

u_char loop = 0;
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop));

并监听 thread2/interface 2 特定接口。 Tcpdump 显示,数据包正在到达,但在我上面的配置中被丢弃。

【问题讨论】:

  • 我认为您必须绑定到的不是 IP,而是绑定到设备,例如setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, "eth0", 4)
  • 如果你让内核为你选择一个接口会发生什么?即使用mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  • @Gene 正确。使用 setsockopt( ... SO_BINDTODEVICE ...) 我可以将 UDP 套接字绑定到接口,但我如何明确地为多播执行此操作? @shinkou 设置为 INADDR_ANY(UDP 和多播订阅)不会改变套接字不接收数据包的事实。在我设置net.ipv4.{all,default,interfaces}.rp_filter = 0 之前,数据包被作为火星人丢弃(在/var/log/syslog 中可见)。似乎与 Linux 相关,因为可以通过 tcpdump 在传出和传入接口上看到数据包。不过现在不知道...
  • 你的路由表是什么样的?当你运行你的监听器时netstat -ng 会说什么?
  • 您可能需要执行setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, ..) 来控制数据包从哪个接口发送...

标签: c sockets interface udp multicast


【解决方案1】:


addr.sin_addr.s_addr=inet_addr(my_ipv4Addr);
bind(sockfd,(SA*)&amp;addr,sizeof(addr));
您只能向多播组发送数据包,
但您无法接收任何数据包,即使是从“my_ipv4Addr”发出的数据包。

所以 addr.sin_addr.s_addr 必须是 htonl(INADDR_ANY)


mreq.imr_interface.s_addr=inet_addr(my_ipv4Addr);
您可以接收来自多播组的所有数据包,
但它使用默认接口(可能是 eth0)发送数据包,
不是您指定的那个(例如 eth1)。
所以这没有效果。


setsockopt(sockfd,SOL_SOCKET,SO_BINDTODEVICE,ETH1,strlen(ETH1));
您可以通过接口 ETH1 发送数据包,
但是您只能接收从与 ETH1 关联的 ip 发出的数据包,
您无法接收来自其他客户端的任何数据包。


mreq.imr_interface.s_addr=inet_addr(my_ipv4Addr);
setsockopt(sockfd,IPPROTO_IP,IP_MULTICAST_IF,&amp;mreq.imr_interface,sizeof(struct in_addr);
您可以通过与 my_ipv4addr 关联的接口发送数据包,
您还可以从多播组中的任何客户端接收任何数据包。

【讨论】:

    【解决方案2】:
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY); 
    //addr.sin_addr.s_addr = inet_addr(my_ipv4Addr); 
    addr.sin_port = htons(port);
    
    mreq.imr_multiaddr.s_addr = inet_addr(group);
    // [-]    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);
    

    你只需要像我一样编辑你的代码。

    【讨论】:

    • 如果您将套接字绑定到 INADDR_ANY,它将接收发送到该特定端口的每个数据包。我将绑定地址保留为 my_IPV4Addr
    【解决方案3】:

    在绑定接收多播流量的套接字时,如果绑定到本地地址,这将防止在非 Windows 系统上接收多播数据包。

    当我发布具有绑定到特定地址功能的UFTP 3.6 版时,我第一次发现了这一点。 Windows 处理得很好,但在 Linux 系统上,应用程序没有收到多播数据包。我不得不在下一个版本中删除该功能。

    请注意,您可以直接绑定到多播地址:

    addr.sin_addr.s_addr = inet_addr(group);
    

    在这种情况下,您将仅在该套接字上接收到该多播地址的流量。但是,您仍然需要在特定接口上加入多播组才能接收。

    如果您计划在同一个套接字上从多个多播地址接收数据,则需要绑定到INADDR_ANY

    【讨论】:

    • 这个答案不正确。我会回顾这个相关的stackoverflow。 stackoverflow.com/questions/10692956/…
    • @f3xy 是正确的。我刚刚在 RedHat 5 系统上验证了它。如果您绑定到INADDR_ANY,您将收到多播。如果您绑定到特定接口,则不会。
    • 声明“当绑定套接字以接收多播流量时,您应该始终绑定到 INADDR_ANY。”是不正确的。如果您阅读我引用的stackoverflow。其中还引用了 Richard Stevens 的 UNIX Network Programming,您将看到您所做的陈述是不正确的。尽管可以绑定 INADDR_ANY 并接收多播,但最好绑定您希望接收的多播地址和端口。也许你可以编辑你的答案?我只是不想让任何人被误导。
    • @f3xy 你是对的,绑定到多播地址确实有效。当您绑定到无法接收多播的本地接口时。我会编辑以反映。
    • 非常感谢您的理解。 UFTP 看起来很整洁,我得去看看。
    【解决方案4】:

    要么你为了理解而简化了你的代码,要么我遗漏了一些东西,

    这是结构

    struct ip_mreqn {
        struct in_addr imr_multiaddr; /* IP multicast group
                                         address */
        struct in_addr imr_address;   /* IP address of local
                                         interface */
        int            imr_ifindex;   /* interface index */
    };
    

    ip man page - IP_ADD_MEMBERSHIP

    但你指的是

    mreq.imr_interface.s_addr = inet_addr(my_ipv4Addr);
    

    什么是imr_interface?可以编译吗?

    如果您只是为了更好的可读性而写了上面的名称,您是否尝试过将接口索引(即 imr_ifindex)填充到您要附加到的特定接口。

    我的猜测是,如果您保留 imrr_address 并仅分配接口索引,它应该绑定到该接口并接收数据包。看看有没有帮助。

    【讨论】:

    • ip_mreqn是新结构,问题用ip_mreq,Linux都支持。
    猜你喜欢
    • 2016-08-30
    • 1970-01-01
    • 2021-08-12
    • 2018-09-05
    • 2014-07-12
    • 2011-07-25
    • 2015-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多