在多宿主 Windows PC 上接收多播消息
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2988788/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Receiving Multicast Messages on a Multihomed Windows PC
提问by Basti
I'm developing a diagnostic tool on a PC with several Network Interfaces based on multicast/udp. The user can select a NIC, the application creates sockets, binds them to this NIC and adds them to the specific multicast group.
我正在基于多播/udp 的具有多个网络接口的 PC 上开发诊断工具。用户可以选择一个 NIC,应用程序创建套接字,将它们绑定到这个 NIC 并将它们添加到特定的多播组。
The sending of multicast messages works fine. However receiving of messages only succeeds if I bind the sockets to a specific NIC of my PC. It almost looks like as there is a 'default' NIC for receiving multicast messages in Windows which is always the first NIC returned by the GetAdapterInfofunction.
多播消息的发送工作正常。但是,只有将套接字绑定到 PC 的特定 NIC 时,才能成功接收消息。它几乎看起来像是在 Windows 中接收多播消息的“默认”NIC,它始终是GetAdapterInfo函数返回的第一个 NIC 。
I monitored the network with Wireshark and discovered that the "IGMP Join Group" message isn't sent from the NIC I bound the socket at, but by this 'default' NIC.
我用 Wireshark 监视网络,发现“IGMP Join Group”消息不是从我绑定套接字的 NIC 发送的,而是通过这个“默认”NIC 发送的。
If I disable this NIC (or remove the network cable), the next NIC of the list returned by GetAdapterInfo is used for receiving multicast messages.
如果我禁用此网卡(或移除网线),则 GetAdapterInfo 返回的列表中的下一个网卡将用于接收多播消息。
I was successful to change this 'default' NIC by adding an additional entry to the routing table of my PC, but I don't think this is a good solution of the problem.
通过向我的 PC 的路由表中添加一个附加条目,我成功地更改了这个“默认”网卡,但我认为这不是问题的一个很好的解决方案。
The problem also occurs with the code appended below. The join group messages isn't sent via 192.168.52 but via a different NIC.
下面附加的代码也会出现此问题。加入组消息不是通过 192.168.52 发送,而是通过不同的 NIC 发送。
// socket_tst.cpp : Defines the entry point for the console application.
//
\#include tchar.h
\#include winsock2.h
\#include ws2ipdef.h
\#include IpHlpApi.h
\#include IpTypes.h
\#include stdio.h
int _tmain(int argc, _TCHAR* argv[])
{
WSADATA m_wsaData;
SOCKET m_socket;
sockaddr_in m_sockAdr;
UINT16 m_port = 319;
u_long m_interfaceAdr = inet_addr("192.168.1.52");
u_long m_multicastAdr = inet_addr("224.0.0.107");
int returnValue = WSAStartup(MAKEWORD(2,2), &m_wsaData);
if (returnValue != S_OK)
{
return returnValue;
}
// Create sockets
if (INVALID_SOCKET == (m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) )
{
return WSAGetLastError();
}
int doreuseaddress = TRUE;
if (setsockopt(m_socket,SOL_SOCKET,SO_REUSEADDR,(char*) &doreuseaddress,sizeof(doreuseaddress)) == SOCKET_ERROR)
{
return WSAGetLastError();
}
// Configure socket addresses
memset(&m_sockAdr,0,sizeof(m_sockAdr));
m_sockAdr.sin_family = AF_INET;
m_sockAdr.sin_port = htons(m_port);
m_sockAdr.sin_addr.s_addr = m_interfaceAdr;
//bind sockets
if ( bind( m_socket, (SOCKADDR*) &m_sockAdr, sizeof(m_sockAdr) ) == SOCKET_ERROR )
{
return WSAGetLastError();
}
// join multicast
struct ip_mreq_source imr;
memset(&imr,0,sizeof(imr));
imr.imr_multiaddr.s_addr = m_multicastAdr; // address of multicastgroup
imr.imr_sourceaddr.s_addr = 0; // sourceaddress (not used)
imr.imr_interface.s_addr = m_interfaceAdr; // interface address
/* first join multicast group, then registerer selected interface as
* multicast sending interface */
if( setsockopt( m_socket
,IPPROTO_IP
,IP_ADD_MEMBERSHIP
,(char*) &imr
, sizeof(imr))
== SOCKET_ERROR)
{
return SOCKET_ERROR;
}
else
{
if( setsockopt(m_socket
,IPPROTO_IP
,IP_MULTICAST_IF
,(CHAR*)&imr.imr_interface.s_addr
,sizeof(&imr.imr_interface.s_addr))
== SOCKET_ERROR )
{
return SOCKET_ERROR;
}
}
printf("receiving msgs...\n");
while(1)
{
// get inputbuffer from socket
int sock_return = SOCKET_ERROR;
sockaddr_in socketAddress;
char buffer[1500];
int addressLength = sizeof(socketAddress);
sock_return = recvfrom(m_socket, (char*) &buffer, 1500, 0, (SOCKADDR*)&socketAddress, &addressLength );
if( sock_return == SOCKET_ERROR)
{
int wsa_error = WSAGetLastError();
return wsa_error;
}
else
{
printf("got message!\n");
}
}
return 0;
}
Thanks four your help!
感谢四位的帮助!
采纳答案by Basti
The problem was a simple typo. Instead of using the structure struct ip_mreq_source, the structure struct ip_mreqmust be used if using the option IP_MULTICAST_IF. (The other structure is needed for the IP_ADD_SOURCE_MEMBERSHIP option)
问题是一个简单的错字。除了使用结构的结构ip_mreq_source,结构结构ip_mreq必须如使用IP_MULTICAST_IF选项一起使用。(IP_ADD_SOURCE_MEMBERSHIP 选项需要另一个结构体)
Using the wrong structure had most probably the result that the setsockeopt function found a zero where the NIC IP address was expected. Zero is also the value of the INADDR_ANY constant, which choose the default NIC of the system. :-)
使用错误的结构很可能导致 setsockeopt 函数在预期的 NIC IP 地址处找到零。零也是 INADDR_ANY 常量的值,它选择系统的默认 NIC。:-)
回答by Cetra
You may want to check/change your routing table. There will be a route for multicast (224.0.0.0, subnet 240.0.0.0) traffic in there with its appropriate metric:
您可能想要检查/更改您的路由表。那里将有一个用于多播(224.0.0.0,子网 240.0.0.0)流量的路由及其适当的度量:
C:\Users\Cetra>netstat -rn
*****
IPv4 Route Table
===========================================================================
Active Routes:
Network Destination Netmask Gateway Interface Metric
0.0.0.0 0.0.0.0 192.168.80.254 192.168.80.99 20
127.0.0.0 255.0.0.0 On-link 127.0.0.1 306
127.0.0.1 255.255.255.255 On-link 127.0.0.1 306
127.255.255.255 255.255.255.255 On-link 127.0.0.1 306
192.168.80.0 255.255.255.0 On-link 192.168.80.99 276
192.168.80.99 255.255.255.255 On-link 192.168.80.99 276
192.168.80.255 255.255.255.255 On-link 192.168.80.99 276
224.0.0.0 240.0.0.0 On-link 127.0.0.1 306
224.0.0.0 240.0.0.0 On-link 192.168.80.99 276
255.255.255.255 255.255.255.255 On-link 127.0.0.1 306
255.255.255.255 255.255.255.255 On-link 192.168.80.99 276
******