首页 > 解决方案 > Linux 多播数据包在 4 分钟后干涸

问题描述

我尝试在 Linux 中处理多播数据包流。对于 266s - 278s(它并不总是完全相同的时间段),接收工作正常,但在那之后,不再接收数据包。

这就是我初始化多播的方式:

int arg = 1;
int fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(fd == -1) {
    fprintf(stderr, "Error creating socket, %s\n", strerror(errno));
    return;
}

if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) {
    fprintf(stderr, "Failed to set `SO_REUSEADDR`, %s\n", strerror(errno));
    return;
}

fcntl(fd, F_SETFL, O_NONBLOCK);

static struct ifreq intf;
strncpy(intf.ifr_name, cfg->ifname_buf, IF_NAMESIZE);

if(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (char*)&intf, sizeof(intf)) < 0) {
    fprintf(stderr, "Failed to set `SO_BINDTODEVICE`, %s\n", strerror(errno));
    return;
}


struct sockaddr_in sin;
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_port = htons(xxxxx);
sin.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");

if(bind(fd, (struct sockaddr*)&sin, sizeof(struct sockaddr)) < 0) {
    fprintf(stderr, "Error on binding socket, %s\n", strerror(errno));
    return;
}

ioctl(fd, SIOCGIFADDR, &intf);

struct ip_mreqn igmpv2_req;
memset(&igmpv2_req, 0, sizeof(struct ip_mreqn));

if(inet_pton(AF_INET, "xxx.xxx.xxx.xxx", &igmpv2_req.imr_multiaddr.s_addr)) {

    memcpy(&igmpv2_req.imr_address, &cfg->ifaddr.sin_addr, sizeof(struct in_addr));
    igmpv2_req.imr_ifindex = cfg->ifindex;

    printf("Multiaddr: %s\n", inet_ntoa(igmpv2_req.imr_multiaddr));
    printf("Interfaceaddr: %s\n", inet_ntoa(igmpv2_req.imr_address));
    printf("Ifindex: %d\n", igmpv2_req.imr_ifindex);

    if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &igmpv2_req, sizeof(struct ip_mreqn)) < 0) {
        fprintf(stderr, "Failed to set `IP_ADD_MEMBERSHIP`: %s\n", strerror(errno));
        return;
    } else {
        printf("Saved FD for igmp socket!\n");
        cfg->socket_fd_igmp = fd;
    }
} else {
    fprintf(stderr, "Failed `inet_pton` igmp-multiaddr, %s\n", strerror(errno));
    return;
}

正如我所说,它适用于 266 - 280 秒。之后,没有收到任何数据包。我不知道这是因为我向多播组添加接口的方式还是因为某些内核队列已满(我每秒最多接收 200k 个数据包)。

标签: clinuxnetworkingmulticast

解决方案


多播不是一件小事。根据您的描述,会发生以下情况:

当你表演

setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &igmpv2_req, sizeof(struct ip_mreqn))

然后客户端为这个组发送一个 igmp 加入消息。您的交换机启用了igmp snooping,现在知道将此组的多播数据包转发到主机的端口。

现在,在您观察到的时间之后(4-5 分钟,260 秒确实是许多交换机的默认值),交换机中的此信息超时,因为主机不会定期/主动发送这些消息。

您需要网络上的多播路由器,它定期查询主机,这些主机以igmp 报告响应它们正在侦听的多播组,以使交换硬件上的 igmp 侦听表保持最新。

或者,您也可以尝试在多播发送方和接收方之间的所有设备上禁用 igmp 侦听,以便将所有多播消息泛洪到所有端口(使它们从第 2 层的角度进行广播)。这会给第 2 层段带来相当高的负载,因此首选第一种方法。

setsockopt()一个丑陋的解决方法(我还没有测试过)可能是每 2-3 分钟删除并重新添加会员资格。这应该强制一个 igmp 数据包,从而保持多播连接,但这不是它应该如何工作的。


推荐阅读