在多宿主 Windows PC 上接收多播消息
我正在一台带有多个基于多播/udp 的网络接口的 PC 上开发一个诊断工具。用户可以选择一个网卡,应用程序创建套接字,将它们绑定到该网卡并将它们添加到特定的多播组。
多播消息的发送工作正常。但是,只有当我将套接字绑定到 PC 的特定网卡时,消息接收才会成功。它几乎看起来就像在 Windows 中存在一个用于接收多播消息的“默认”NIC,它始终是 GetAdapterInfo 函数返回的第一个 NIC。
我使用 Wireshark 监视网络,发现“IGMP 加入组”消息不是从我绑定套接字的 NIC 发送的,而是通过这个“默认”NIC 发送的。
如果我禁用此 NIC(或拔掉网络电缆),则 GetAdapterInfo 返回的列表中的下一个 NIC 将用于接收多播消息。
我通过向我的 PC 的路由表中添加一个附加条目成功地更改了此“默认”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;
}
谢谢四位的帮助!
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.
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 GetAdapterInfo function.
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.
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.
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.
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.
// 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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
问题是一个简单的拼写错误。
如果使用选项 IP_MULTICAST_IF,则必须使用结构 struct ip_mreq,而不是使用结构 struct ip_mreq_source。 (IP_ADD_SOURCE_MEMBERSHIP 选项需要另一个结构)
使用错误的结构很可能导致 setsockeopt 函数在预期的 NIC IP 地址处发现零。零也是 INADDR_ANY 常量的值,它选择系统的默认网卡。 :-)
The problem was a simple typo.
Instead of using the structure struct ip_mreq_source, the structure struct ip_mreq must be used if using the option IP_MULTICAST_IF. (The other structure is needed for the IP_ADD_SOURCE_MEMBERSHIP option)
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. :-)
您可能想检查/更改您的路由表。其中将有一个多播(224.0.0.0,子网 240.0.0.0)流量的路由及其适当的度量:
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: