【问题标题】:Receiving Multicast Messages on a Multihomed Windows PC在多宿主 Windows PC 上接收多播消息
【发布时间】:2010-06-07 10:44:10
【问题描述】:

我正在使用基于多播/udp 的多个网络接口的 PC 上开发一种诊断工具。用户可以选择一个 NIC,应用程序创建套接字,将它们绑定到这个 NIC 并将它们添加到特定的多播组。

多播消息的发送工作正常。但是,只有当我将套接字绑定到我的 PC 的特定 NIC 时,才能成功接收消息。它几乎看起来像在 Windows 中有一个用于接收多播消息的“默认”NIC,它始终是 GetAdapterInfo 函数返回的第一个 NIC。

我使用 Wireshark 监控网络,发现“IGMP 加入组”消息不是从我绑定套接字的 NIC 发送的,而是通过这个“默认”NIC。

如果我禁用此网卡(或移除网线),GetAdapterInfo 返回的列表中的下一个网卡将用于接收多播消息。

我通过在我的 PC 的路由表中添加一个附加条目成功地更改了这个“默认”网卡,但我认为这不是解决问题的好方法。

下面附加的代码也会出现问题。加入群组消息不是通过 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;
}

感谢四位的帮助!

【问题讨论】:

    标签: windows sockets udp multicast multihomed


    【解决方案1】:

    问题是一个简单的错字。 如果使用选项 IP_MULTICAST_IF,则必须使用结构 struct ip_mreq,而不是使用结构 struct ip_mreq_source。 (IP_ADD_SOURCE_MEMBERSHIP 选项需要其他结构)

    使用错误的结构很可能导致 setsockeopt 函数在预期 NIC IP 地址的位置找到零。零也是 INADDR_ANY 常量的值,它选择系统的默认 NIC。 :-)

    【讨论】:

      【解决方案2】:

      您可能想要检查/更改您的路由表。那里将有一条用于多播(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
      
      ******
      

      【讨论】:

      • 感谢您的回复。向路由表添加/更改多播条目是一种有效的解决方法。但很可能我无法更改客户 PC 的路由表。
      • 我不认为这是一种解决方法,这就是网络的工作方式。您客户的 PC 也会多宿主吗?
      • 由于应用程序是一个产品,我不能排除它。我已经考虑过通过 Windows API 函数更改路由表......但我不确定这是否不会对客户 PC 产生一些副作用。所以我放弃了这个想法。
      • 好吧,如果您自动更改它,它就不会受到太多信任。如果您的设置对话框中有一些内容列出了所有接口,并且在更改时有免责声明,即“这将更改多播流量的路由,您确定吗?y/n”。
      • 是的,那将是一个更好的方法 :-) 但是,我仍然无法相信没有办法强制套接字使用特定的 NIC 发送多播 IGMP 加入消息。这对我来说没有任何意义。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-19
      • 1970-01-01
      • 2011-07-07
      • 1970-01-01
      • 2016-10-15
      • 2012-06-05
      相关资源
      最近更新 更多