您如何“取消” UdpClient::BeginReceive?

Posted

技术标签:

【中文标题】您如何“取消” UdpClient::BeginReceive?【英文标题】:How do you 'cancel' a UdpClient::BeginReceive? 【发布时间】:2013-08-21 00:34:35 【问题描述】:

我有一个线程等待来自多个接口的 UDP 消息,使用 UdpClient::BeginReceive 和一个调用 UdpClient::EndReceive 的回调来获取数据并将其传递。

如果 5 秒后我什么也没得到,我会从调用 UdpClient::BeginReceive 的函数返回,以便可以取消进程并发出另一个广播,这将触发外部客户端发送 UDP 响应。如果我们不取消,我会再次调用UdpClient::BeginReceive 函数来检查新数据。

如果客户端没有及时收到任何数据,有没有办法取消该异步请求而无需调用无限期阻塞的EndReceive?我的印象是让数百个异步触发器运行是个坏主意。

【问题讨论】:

【参考方案1】:

您需要猛拉地垫,调用 UdpClient.Close() 方法。

这将完成 BeginReceive() 调用并且您的回调方法将运行。当您调用 EndReceive() 时,该类会告知您地垫已消失,并将引发 ObjectDisposedException。所以准备好处理这个异常,用 try/catch 包装 EndReceive() 调用,这样你就可以捕获 ODE 并退出回调方法。

请注意,这往往会让 .NET 程序员有些不安,通常强烈建议不要使用异常进行流控制。但是,您必须调用 EndInvoke(),即使您已经知道这会引发异常。否则会导致资源泄漏,可能会持续一段时间。

【讨论】:

这几乎是我发布问题后得出的结论。我的实现现在不是最干净的。真的,我认为我应该让每个接口运行一个侦听器,并且只使用 Close() 关闭,正如您在需要正确处理事情时所建议的那样。感谢您的确认。【参考方案2】:

对于取消BeginRecieve,您可以使用Close 方法,但在基本实现中它将调用ObjectDisposedException。我通过创建自定义 BeginRecieve 来修复它,这是基础实现的现代化:

public Task<UdpReceiveResult> ReceiveAsync(UdpClient client, CancellationToken breakToken)
    => breakToken.IsCancellationRequested
        ? Task<UdpReceiveResult>.Run(() => new UdpReceiveResult())
        : Task<UdpReceiveResult>.Factory.FromAsync(
            (callback, state) => client.BeginReceive(callback, state),
            (ar) =>
                
                    if (breakToken.IsCancellationRequested)
                        return new UdpReceiveResult();

                    IPEndPoint remoteEP = null;
                    var buffer = client.EndReceive(ar, ref remoteEP);
                    return new UdpReceiveResult(buffer, remoteEP);
                ,
            null);

阅读更多:UdpClient.ReceiveAsync correct early termination。

这也会有所帮助:Is it safe to call BeginXXX without calling EndXXX if the instance is already disposed。

【讨论】:

【参考方案3】:
      // Set ReadMsg = true while using UdpClient
      // Set ReagMsg = false before Dispose or Close no Exception Error
      private static bool ReadMsg;
      private static void ReceiveCallback(IAsyncResult ar)
      

        UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u;
        IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
        try
        
            if ( ReadMsg )
            
                Byte[] receiveBytes = u.EndReceive(ar, ref e);
                string sddpMessage = Encoding.ASCII.GetString(receiveBytes);

            
        
        catch (Exception ex)
        
            Debug.WriteLine($"ex.Message");
        
    

【讨论】:

欢迎来到 SO。请添加更多信息此代码的作用以及解决问题的原因。

以上是关于您如何“取消” UdpClient::BeginReceive?的主要内容,如果未能解决你的问题,请参考以下文章

您如何在 crontab 中取消设置 MAILTO?

如何在 xcode 中快速取消选择除您想要的构建目标之外的所有构建目标

pyspark如何使用两个标题行取消转csv [重复]

如何取消表格单元格选择

如何在 Relay Modern 中取消订阅

如何在 TortoiseSVN 中取消忽略文件?