有多个网卡时组播收不到消息
遇到组播有概率收不到消息的问题,经分析是由于没有指定网络接口导致的。
该设备上有两个网卡,组播消息来自 192.168.32.31 所在的网络:
1> ipconfig
2
3Windows IP Configurtion
4
5Ethernet adapter 192.168.31.31
6
7 Connection-specific DNS Suffix . :
8 IP Address. . . . . . . . . . . . : 192.168.31.31
9 Subnet Mask . . . . . . . . . . . : 255.255.255.0
10 Default Gateway . . . . . . . . . :
11
12Ethernet adapter 192.168.32.31
13
14 Connection-specific DNS Suffix . :
15 IP Address. . . . . . . . . . . . : 192.168.32.31
16 Subnet Mask . . . . . . . . . . . : 255.255.255.0
17 Default Gateway . . . . . . . . . :UDP Socket 绑定了 192.168.32.31 然后加入组播,实际上并不表示从 192.168.32.31 接收组播。
UDP 是无连接的,bind 函数只是 recv 时省略 IP 地址和端口号。
要从指定的网络接口接收组播,需要 bind 0.0.0.0 或组播地址,然后在加入组播时指定网络接口。
以 Linux 的 Socket API 为例:
1// 1. bind 时推荐使用 INADDR_ANY (0.0.0.0)
2struct sockaddr_in local_addr;
3memset(&local_addr, 0, sizeof(local_addr));
4local_addr.sin_family = AF_INET;
5local_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 不要写 192.168.32.31,可以写组播地址
6local_addr.sin_port = htons(8888); // 组播端口
7bind(sock, (struct sockaddr*)&local_addr, sizeof(local_addr));
8
9// 2. 在 setsockopt 中显式指定 192.168.32.31
10struct ip_mreqn mreqn;
11mreqn.imr_multiaddr.s_addr = inet_addr("229.0.0.5"); // 组播 IP
12mreqn.imr_interface.s_addr = inet_addr("192.168.32.31"); // 指定接收组播的网卡 IP
13mreqn.imr_ifindex = if_nametoindex("eth0"); // 指定接口
14setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof(mreqn));imr_interface和imr_ifindex均可指定网络接口,只填一个即可,后者通常用于暂未分配地址或地址动态变化(如 DHCP)的接口。
以 Qt 的 QUdpSocket 为例,应当调用下面第二个函数手动指定网络接口:
1// 自动选择网络接口
2bool joinMulticastGroup(const QHostAddress &groupAddress);
3
4// 手动选择网络接口
5bool joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface);