Получение многоадресных сообщений на многосетевом ПК с Windows
Я разрабатываю диагностический инструмент на ПК с несколькими сетевыми интерфейсами на основе multicast/udp. Пользователь может выбрать NIC, приложение создает сокеты, связывает их с этим NIC и добавляет их в конкретную многоадресную группу.
Отправка многоадресных сообщений работает нормально. Однако получение сообщений будет успешным только в том случае, если я подключу сокеты к определенной сетевой карте моего компьютера. Это почти похоже на то, что в Windows есть сетевой адаптер по умолчанию для получения многоадресных сообщений, который всегда является первым сетевым адаптером, возвращаемым функцией GetAdapterInfo.
Я проверил сеть с помощью Wireshark и обнаружил, что сообщение "IGMP Join Group" отправляется не с сетевой карты, на которой я привязал сокет, а с помощью этой сетевой карты "по умолчанию".
Если я отключу этот NIC (или удалим сетевой кабель), следующий NIC из списка, возвращенного GetAdapterInfo, будет использоваться для получения многоадресных сообщений.
Мне удалось изменить этот сетевой адаптер "по умолчанию", добавив дополнительную запись в таблицу маршрутизации моего компьютера, но я не думаю, что это хорошее решение проблемы.
Проблема также возникает с кодом, добавленным ниже. Сообщения группы присоединения отправляются не через 192.168.52, а через другой сетевой адаптер.
// 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;
}
Спасибо за вашу помощь!
2 ответа
Проблема была в простой опечатке. Вместо использования структуры struct ip_mreq_source, структура struct ip_mreq должна использоваться, если используется опция IP_MULTICAST_IF. (Другая структура необходима для опции IP_ADD_SOURCE_MEMBERSHIP)
Использование неправильной структуры, скорее всего, привело к тому, что функция setsockeopt нашла ноль там, где ожидался IP-адрес NIC. Ноль также является значением константы INADDR_ANY, которая выбирает сетевой адаптер по умолчанию для системы.:-)
Вы можете проверить / изменить свою таблицу маршрутизации. Там будет маршрут для многоадресного (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
******