【问题标题】:UdpClient.BeginReceive() does not receive unless it haven't sent shortly beforeUdpClient.BeginReceive() 不接收,除非它不久前没有发送
【发布时间】:2015-10-22 08:10:14
【问题描述】:

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

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

我的预期是:

  1. 从站请求数据
  2. Master 发送数据
  3. Slave 接收和消费
  4. Master 等待 2-3 分钟
  5. Master 发送新数据
  6. Slave 接收并消费新数据
  7. 重复从 4 到 6 的步骤

我看到的是:

  1. 从站没有收到任何东西

但是如果我让从站不断地请求新数据(轮询,即重复第 1 步),我终于得到了消息。

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

从属应用在异步模式下使用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 之前发送过东西。

【问题讨论】:

  • 一个疯狂的猜测,但它可能与ExclusiveAddressUseReuseAddress 有关吗?尝试反转这些值,看看是否可行。
  • 我会尝试删除套接字选项。你在使用超时吗?我认为它不会影响异步接收,但如果它影响它可能是原因。
  • 我试图删除所有套接字选项,但应用程序仍然没有收到数据包...
  • 记录器中的输出给了你什么?
  • (state.udpClient.BeginReceive 可能直接在同一个线程中返回,如果数据立即可用)

标签: c# network-programming multicast udpclient


【解决方案1】:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-10
    • 2019-02-07
    • 2012-08-24
    • 2017-09-11
    • 2015-08-08
    • 1970-01-01
    相关资源
    最近更新 更多