UdpClient.BeginReceive()除非事先不久发送,否则不会收到

时间:2015-10-22 08:10:15

标签: c# network-programming multicast udpclient

我正在调试一个奇怪的问题,发生在实时环境中的一台机器上。

我的应用程序(从属)应该在任何时候从LAN中的另一台主机(主机)接收UDP多播消息,但显然只有在从机先前已发送消息时才会这样做。

我的期望是:

  1. Slave要求提供数据
  2. Master发送数据
  3. 奴隶接收并消费
  4. 大师等待2-3分钟
  5. Master发送新数据
  6. Slave接收并使用新数据
  7. 重复步骤4到6
  8. 我看到的是:

    1. 奴隶没有收到任何东西
    2. 但如果我让奴隶连续要求新数据(轮询,即重复步骤1),我终于收到了消息。

      我在Wireshark中看到来自主服务器的消息确实是由从属主机接收的。只是我的应用程序没有收到它。更令人惊讶的是,在同一网络上运行的另一个主从对,使用相同的应用程序,工作正常,以及我在测试环境中的配对。

      slave应用程序在异步模式下使用UdpClient。以下是监听器的初始化方式:

      private void ListenMain()
      {
          try
          {
              UdpClient udpClient = new UdpClient();
              udpClient.Client.ExclusiveAddressUse = false;
              udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
              udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);
              IPv4InterfaceProperties p = adapter.GetIPProperties().GetIPv4Properties();
      
              udpClient.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, (int)IPAddress.HostToNetworkOrder(p.Index));
      
              udpClient.Client.Bind(endPoint);
              udpClient.JoinMulticastGroup(12345);
      
              ListenState listenState = new ListenState();
              listenState.udpClient = udpClient;
              listenStates.Add(listenState);
              logger.Debug("Waiting for messages");
              udpClient.BeginReceive(new AsyncCallback(OnPacketReceived), listenState);
          }
          catch (Exception e)
          {
              logger.Error(e, "ListenMain() encountered an error");
          }
      }
      

      这是收到的数据包的处理程序:

      private void OnPacketReceived(IAsyncResult result)
      {
          logger.Trace("OnPacketReceived");
          IPEndPoint recvAddress = new IPEndPoint(IPAddress.Any, MULTICAST_PORT);
          ListenState state = result.AsyncState as ListenState;
          byte[] receive_byte_array;
          try
          {
              logger.Trace("before EndReceive");
              receive_byte_array = state.udpClient.EndReceive(result, ref recvAddress);
              logger.Trace("after EndReceive, got {0} bytes", receive_byte_array.Length);
      
              // packet handling goes here...
      
              // do the loop
              logger.Trace("waiting for another packet");
              state.udpClient.BeginReceive(new AsyncCallback(OnPacketReceived), state);
          }
          catch (ObjectDisposedException)
          {
              logger.Info("Socket is now closed");
              return;
          }
          catch (Exception e)
          {
              logger.Warn(e, "exception in handling incoming message");
          }
      }
      

      当然,轮询新数据不是最佳解决方案,并会引入不必要的延迟。我想知道哪个现象会导致UdpClient丢失传入的数据包,除非相同的UdpClient之前发送了一些内容。

1 个答案:

答案 0 :(得分:0)

我认为代码中有错误:udpClient.JoinMulticastGroup(); 将多播 IP 地址作为参数,而不是端口。当你解决这个问题时它会起作用吗?如果是这样,这说明了:

不加入多播组会导致典型的“未加入多播组”的不稳定行为,其中包括最喜欢的“它工作两到五分钟然后突然停止”和“当我向另一个方向发送内容时它工作然后突然停止”和“它在使用不同的多播地址时工作然后停止,留下不可用的多播地址”。

您看到的行为是具有或多或少智能路由器和交换机的 IPv4 多播的典型行为。它们都支持某些版本的 IGMP 侦听(具有超时、错误和不兼容的版本),并且路由器、交换机和操作系统缓存网络路径和 MAC 以及注册和未注册的多播 IP 一段不确定的时间。这使得无法以合乎逻辑的方式对行为进行推理。

检查您是否在接收器/侦听器上加入了预期的多播组。当这看起来没问题但您仍然有问题时,请跟踪 IGMP 消息并查找任何没有意义的东西,例如从未看到加入或看到不稳定的叶子。

(请注意,IGMP 消息由操作系统在机器级别发送,而不是由您的应用程序发送。这意味着并非每个 JoinMulticastGroup() 都会生成 IGMP 加入消息。)