UdpClient.BeginReceive()不会接收,除非它之前没有发送过

本文关键字:BeginReceive UdpClient | 更新日期: 2023-09-27 18:18:43

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

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

我期望的是:

  1. 从服务器请求数据
  2. 主机发送数据
  3. Slave接收和消耗
  4. 主端等待2-3分钟
  5. 主机发送新数据
  6. Slave接收并使用新数据
  7. 步骤4到6重复

我看到的是:

  • Slave没有收到任何信息
  • 但是如果我让slave连续请求新数据(轮询,即重复步骤1),我最终得到消息。

    我在Wireshark中看到,从主机确实收到了来自主主机的消息。只是我的应用没有接收到。更令人惊讶的是,在同一网络上运行相同应用程序的另一个主从对运行良好,测试环境中的我的对也是如此。

    从端应用异步使用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之前发送了一些东西。

    UdpClient.BeginReceive()不会接收,除非它之前没有发送过

    我认为代码中存在错误:udpClient.JoinMulticastGroup();将多播IP地址作为参数,而不是端口。当你修复这个的时候,它能工作吗?如果是,解释如下:

    未加入组播组将导致典型的"未加入组播组";行为不稳定,其中包括最喜欢的"它工作了两到五分钟,然后突然停止";当我向另一个方向发送一些东西,然后突然停止时,它会起作用。并且"它在使用不同的组播地址时工作,然后停止,留下不可用的组播地址"。

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

    检查您是否在接收端/侦听端加入了预期的多播组。如果这看起来没问题,但您仍然有问题,请跟踪IGMP消息并查找任何不合理的内容,例如从未看到连接,或看到不稳定的叶子。

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