Réception de messages Multicast sur un PC Windows multirésident
-
24-10-2019 - |
Question
Je développe avec plusieurs interfaces réseau un outil de diagnostic sur un PC basé sur la multidiffusion / udp. L'utilisateur peut sélectionner une carte réseau, l'application crée les sockets, les lie à cette carte réseau et les ajoute au groupe de multidiffusion spécifique.
L'envoi de messages de multidiffusion fonctionne très bien. Cependant la réception de messages ne réussit que si je lie les prises à une carte réseau spécifique de mon PC. Il ressemble presque comme il y a une carte réseau « par défaut » pour recevoir des messages de multidiffusion dans Windows qui est toujours la première carte réseau retournée par le GetAdapterInfo fonction.
J'ai suivi le réseau avec Wireshark et a découvert que le message « IGMP Rejoindre le groupe » n'est pas envoyé de la carte réseau I lié à la prise, mais ce « défaut » NIC.
Si je désactive cette carte réseau (ou supprimer le câble réseau), la prochaine carte réseau de la liste renvoyée par GetAdapterInfo est utilisé pour recevoir des messages de multidiffusion.
J'a réussi à changer cette carte réseau « par défaut » en ajoutant une entrée supplémentaire à la table de routage de mon PC, mais je ne pense pas que ce soit une bonne solution du problème.
Le problème se produit également avec le code en annexe ci-dessous. La jointure des messages de groupe ne sont pas envoyés par 192.168.52 mais via une carte réseau différente.
// 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;
}
Merci quatre votre aide!
La solution
Le problème était une typo simple. Au lieu d'utiliser la structure struct ip_mreq_source , la structure struct ip_mreq doit être utilisé si vous utilisez l'option IP_MULTICAST_IF. (L'autre structure est nécessaire pour l'option IP_ADD_SOURCE_MEMBERSHIP)
En utilisant la mauvaise structure devait très probablement le résultat que la fonction setsockeopt trouve un zéro où l'adresse IP NIC était attendue. Zero est également la valeur de la constante INADDR_ANY, qui choisissent la carte réseau par défaut du système. : -)
Autres conseils
Vous pouvez vérifier / modifier votre table de routage. Il y aura une route pour la multidiffusion (224.0.0.0, sous-réseau 240.0.0.0) le trafic là-bas avec sa métrique appropriée:
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
******