多网卡下,C++UDP指定源组播收不到流,原因排查

  • 首先,指定源组播,linux 和windows编程稍微有些不同:

                   Linux:bind的是组播地址和组播端口

                   windows:bind的是接收网卡的地址(local_ip)和组播端口

  • 对于一个网卡收流,其他网卡不用收流

          现象1):组播收不到流。

              原因:标红的语句填写的IP地址是any了,如果默认IP不是要收组播的网卡IP,就会收不到流。

              解决方法:把srcMreq.imr_interface.s_addr 改成本地IP,即可收到流。

    struct ip_mreq_source srcMreq;
    srcMreq.imr_multiaddr.s_addr = inet_addr(muticast_ip.c_str());//组播地址
    srcMreq.imr_interface.s_addr = inet_addr(local_ip.c_str());//本地网卡的地址
    srcMreq.imr_sourceaddr.s_addr = inet_addr(src_ip.c_str());//组播的指定源地址
    if (0 > setsockopt(h_sock, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, (char*)&srcMreq, sizeof(srcMreq)))
    {
        cout << "setsockopt IP_ADD_SOURCE_MEMBERSHIP failed, ";
        return false;
    }

         现象2) :可以收到组播流,但是接收一段时间,就收不到了。时间一般是路由器配置查询器的时间的2倍。

                原因:网卡未添加组播路由,所以没有持续发IGMP包,所以路由器查询后,发现没有端口收流,就不继续往端口发流了

                 解决方法:route add -net 0.0.0.0 netmask 0.0.0.0 dev eth0

  • 其次:对于要多网卡收流

        现象描述:

                    网卡1(192.168.100.71) :可以收到组播地址的流(source 192.168.100.150);网卡2(192.168.1.171):收不到组播地址的流(source 192.168.100.150)

                   原因: 网卡2发送不了IGMP的包,原因1.171不知道里没有100.150的路由,所以不知道往哪里发送,解决方法:route add -net 192.168.100.150 netmask 255.255.255.255 dev enp1s0f2,这样网卡2可以收到组播包,但是网卡1收不到组播包了。

                   因此要想多网卡都接收组播流,配置路由就不行了。

                   我还尝试了 sysctl -w net.ipv4.conf.all.rp_filter=2,当把所有网卡的的内核参数都设置2后,网卡enp1s0f2可以收到流了,但是收一段时间后,流就停了,用tcpdump -i enp1s0f2 igmp  -l -n -vv 观察,enp1s0f2仅仅收到交换机发送的igmp查询包,却没有enp1s0f2发送的IGMP的包(只有刚开始加入组的时候有两个IGMP的包,正常情况应该是加入的时候发送两个IGMP包,然后每隔一段时间,再发送一个IGMP包)。

                    后来运行下面的命令,就可以多网卡收流:

                     sysctl -w net.ipv4.conf.all.rp_filter=0
                    cat /proc/sys/net/ipv4/conf/all/rp_filter

                    切记:有几个网卡,就要执行几次sysctl -w net.ipv4.conf.网卡名.rp_filter=0,每个网卡执行一下次

-----------------------------------------------------------------------------------------------------------------------------------------------------------------

  •     rp_filter参数详细介绍

                rp_filter参数有三个值,0、1、2,具体含义:

                 0:不开启源地址校验。

                 1:开启严格的反向路径校验。对每个进来的数据包,校验其反向路径是否是最佳路径。如果反向路径不是最佳路径,  则直接丢弃该数据包。

                 2:开启松散的反向路径校验。对每个进来的数据包,校验其源地址是否可达,即反向路径是否能通(通过任意网口),如果反向路径不同,则直接丢弃该数据包

             

           如上所示,数据包发到了eth1网卡,如果这时候开启了rp_filter参数,并配置为1,则系统会严格校验数据包的反向路径。从路由表中可以看出,返回响应时数据包要从eth0网卡出,即请求数据包进的网卡和响应数据包出的网卡不是同一个网卡,这时候系统会判断该反向路径不是最佳路径,而直接丢弃该请求数据包。(业务进程也收不到该请求数据包)

                解决办法:

                1.修改路由表,使响应数据包从eth1出,即保证请求数据包进的网卡和响应数据包出的网卡为同一个网卡。

                2.关闭rp_filter参数。(注意all和default的参数都要改)

                               1)修改/etc/sysctl.conf文件,然后sysctl -p刷新到内存。

                               2)使用sysctl -w直接写入内存:sysctl -w net.ipv4.conf.all.rp_filter=0

                              3)修改/proc文件系统: echo "0">/proc/sys/net/ipv4/conf/all/rp_filter

                 rp_filters参数介绍来自https://www.cnblogs.com/lipengxiang2009/p/7446388.html

    多网卡收同一个组播流,当rp_filter=0时,每个网卡上收到两份数据
             

   把socket绑定网卡,让socket接收到该网卡的网络包

    struct ifreq Ifreq;
    strcpy(Ifreq.ifr_name, "eth0"); //这里指定使用那块网卡拉流 参数为网卡名称

    if (setsockopt(h_sock, SOL_SOCKET, SO_BINDTODEVICE, (char *)&Ifreq, sizeof(Ifreq)) < 0)
    {
        perror("setsockopt():SO_BINDTODEVICE");
        return false;
    }

 

指定UDP通过指定网卡进行组播通讯,需要在创建UDP套接字时绑定指定网卡IP地址和端口号。以下是一个使用C++实现的示例代码: ```c++ #include <iostream> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <net/if.h> #define GROUP "239.0.0.1" // 组播组IP地址 #define PORT 8888 // 组播组端口号 int main() { int sockfd; struct sockaddr_in addr; char msg[1024]; // 创建UDP套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket error"); exit(-1); } // 绑定指定网卡IP地址和端口号 struct sockaddr_in local_addr; memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_addr.s_addr = inet_addr("192.168.1.100"); // 指定网卡IP地址 local_addr.sin_port = htons(PORT); if (bind(sockfd, (struct sockaddr*)&local_addr, sizeof(local_addr)) == -1) { perror("bind error"); exit(-1); } // 加入组播组 struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(GROUP); mreq.imr_interface.s_addr = inet_addr("192.168.1.100"); // 指定网卡IP地址 if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) { perror("setsockopt error"); exit(-1); } // 循环发送数据 while (true) { std::cout << "Enter message to send: "; std::cin.getline(msg, 1024); int len = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&addr, sizeof(addr)); if (len == -1) { perror("sendto error"); exit(-1); } } // 关闭套接字 close(sockfd); return 0; } ``` 在上面的代码中,我们使用`inet_addr`函数将指定网卡的IP地址转换为网络字节序,并使用`bind`函数将UDP套接字绑定到该地址和端口号上。然后,我们使用`IP_ADD_MEMBERSHIP`选项将套接字加入到指定组播组中。最后,我们通过`sendto`函数发送数据到组播组中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值