多网卡下,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;
    }

 
————————————————
版权声明:本文为CSDN博主「liuliu123456」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/liuliu123456/article/details/105486442

你可能感兴趣的:(多网卡下,C++UDP指定源组播收不到流,原因排查)