Ricezione multicast messaggi su un PC Windows Multihomed
-
24-10-2019 - |
Domanda
sto sviluppando uno strumento di diagnostica su un PC con interfacce diverse di rete sulla base di multicast / UDP. L'utente può selezionare una scheda di rete, l'applicazione crea prese, li lega a questa scheda di rete e li aggiunge al gruppo specifico multicast.
L'invio di messaggi multicast funziona bene. Tuttavia la ricezione di messaggi riesce solo se si legano le prese ad una specifica scheda di rete del mio PC. Sembra quasi quanto v'è un 'default' NIC per la ricezione di messaggi multicast in Windows, che è sempre il primo NIC restituito dalla GetAdapterInfo la funzione.
I monitorato la rete con Wireshark e ha scoperto che il messaggio "IGMP Iscriviti al gruppo" non viene inviato dalla scheda di rete ho legato la presa in, ma da questo 'default' NIC.
Se rendo invalido questa scheda di rete (o rimuovere il cavo di rete), il prossimo NIC della lista restituita da GetAdapterInfo viene utilizzato per la ricezione dei messaggi multicast.
I era riuscito a cambiare questo 'default' NIC con l'aggiunta di una voce aggiuntiva alla tabella di routing del mio PC, ma non credo che questa è una soluzione buona del problema.
Il problema si verifica anche con il codice aggiunto sotto. Il join messaggi di gruppo non vengono inviati via 192.168.52 ma tramite una scheda di rete diversa.
// 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;
}
Grazie a quattro il vostro aiuto!
Soluzione
Il problema era un semplice errore di battitura. Invece di utilizzare la struttura di struct ip_mreq_source , la struttura di struct ip_mreq deve essere utilizzato se si utilizza l'opzione IP_MULTICAST_IF. (È necessaria l'altra struttura per l'opzione IP_ADD_SOURCE_MEMBERSHIP)
Usando la struttura sbagliata ha avuto la meglio, probabilmente il risultato che la funzione setsockeopt trovato uno zero in cui era previsto l'indirizzo IP NIC. Zero è anche il valore della costante INADDR_ANY, che scegliere la scheda di rete di default del sistema. : -)
Altri suggerimenti
Si consiglia di controllare / modificare la tabella di routing. Ci sarà un percorso per il traffico multicast (224.0.0.0, sottorete 240.0.0.0) in là con la sua appropriata metrica:
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
******