我有一台电脑有三个网络接口--一个真实的和两个虚拟的VMWare。我想从端口1900上的UPNP设备接收多播消息。
我尝试枚举所有适配器,并为每个适配器创建一个套接字。另外,我设置了套接字选项ReuseAddr,关闭了ExclusiveAddrUse套接字选项,并将每个套接字添加到组播组239.255.255.250。并且我将socket绑定到addr:interfaceaddr:1900
问题是只有一个套接字接收消息--其中一个VMWare net套接字。
Proto Local address Peer address PID App
UDP 0.0.0.0:1900 *:* 5248 uTorrent
UDP 127.0.0.1:1900 *:* 3932 myApp
UDP 127.0.0.1:1900 *:* 1400 svchost
UDP 192.168.0.100:1900 *:* 3932 myApp
UDP 192.168.0.100:1900 *:* 1400 svchost
UDP 192.168.139.1:1900 *:* 1400 svchost
UDP 192.168.139.1:1900 *:* 3932 myApp +
UDP 192.168.180.1:1900 *:* 1400 svchost
UDP 192.168.180.1:1900 *:* 3932 myApp
只有标记为'+'的套接字接收UPNP多播。但Wireshark显示有许多其他数据包未被我接收。我错在哪里?
UPD1我的代码正在主机上运行(Windows 7),并且当时没有虚拟机运行。我有一些UPNP设备(路由器等)在我的真实网络-192.168.0.*也是发送一些通知,但我没有收到他们。通过虚拟网络192.168.139.*的UPNP通知消息由主机(ms Player/Renderer/ETC)发送。这样的通知也会通过所有可用的网络发送,但我只在192.168.139.1接口上收到它们。
UPD2我重写代码以使用单个套接字并将其绑定到inaddr_any:1900。第一次,当我启动新版本时,所有的工作都很好--我通过虚拟或真实的网络接收来自所有设备的所有UPNP消息。但是下一次当我启动我的应用程序时,我可以看到只收到来自192.168.139.*net的通知--这一切都与启动问题相同。
UPD3我关闭了所有的虚拟网络适配器和保持只有真正的适配器。在这种配置中,两种版本的代码(绑定到所有地址的一个套接字/每个接口一个套接字)都可以正常工作。
关于MCVE,制造MCVE并不是那么容易,所以现在我试图通过症状来理解发生了什么。
伪码
hSock = :: socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
u_long nb = 1;
::ioctlsocket( hSock, FIONBIO, &nb);
reuseAddrSet(hSock, TRUE);
reusePortSet(hSock, TRUE);
if (useSingleSocket)
bindSocket(hSock, "any-addr");
else
bindSocket(hSock, "interface-addr");
broadcastModeSet(hSock, TRUE);
setSockOption( hSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, { "any-addr", "239.255.255.250" } );
UPD4我找到了关于多播的下一个注释(此处-http://www4.ncsu.edu/rhee/clas/csc495j/mcast.api.txt:
每个成员身份都与单个接口相关联,并且有可能在多个接口上加入同一组。“imr_interface”应为INADDR_ANY以选择默认多播接口,或主机的本地地址之一以选择特定的(支持多播的)接口。最多可以在单个套接字上添加IP_MAX_MEMBERSHIPS(当前为20个)成员身份。
如上注所示,守则
setSockOption( hSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, { "any-addr", "239.255.255.250" }
只将默认多播接口添加到多播组,而不是像我最初假设的那样为所有接口添加。因此,我需要枚举接口并调用
setSockOption( hSock, IPPROTO_IP, IP_ADD_MEMBERSHIP, { "interface-addr", "239.255.255.250" }
对于单个套接字,或者使用与绑定Soxket所用的相同地址为每个per-interface套接字调用它。
我说对了?
正如UPD4中所写的,套接字用户必须使用接口地址将套接字添加到组播组中。在向组播组添加套接字时使用INADDR_ANY只向组播组添加默认组播接口,因此仅有一个接口就可以接收组播消息。
此外,正如我们所知,操作系统对套接字的组播组的数量有限制,所以更好的方法是为每个网络接口创建一个套接字。