C# 如何知道UdpClient是否已关闭/处置?

C# 如何知道UdpClient是否已关闭/处置?,c#,asynchronous,dispose,udpclient,C#,Asynchronous,Dispose,Udpclient,我通过通常的异步回调从UdpClient接收数据: private void OnUdpData(IAsyncResult result) { byte[] data = _udpReceive.EndReceive(result, ref _receiveEndPoint); //Snip doing stuff with data _udpReceive.BeginReceive(OnUdpData, null); } 当我Close()主线程中的UdpClie

我通过通常的异步回调从UdpClient接收数据:

private void OnUdpData(IAsyncResult result)
{
    byte[] data = _udpReceive.EndReceive(result, ref _receiveEndPoint);

    //Snip doing stuff with data

    _udpReceive.BeginReceive(OnUdpData, null);
}
当我
Close()
主线程中的UdpClient时,回调会像我预期的那样触发,但此时
\u udpreceptive
已经被释放,当我尝试调用
EndReceive()
时,我会得到一个
ObjectDisposedException
。我本来希望得到一个空的缓冲区


正确的处理方法是什么?是否有
UdpClient
的某个成员可以在尝试使用它之前进行检查,或者它是将其全部包装在
try{}
中并捕获
ObjectDisposedException
的唯一方法?对于正常的收盘来说,这似乎很糟糕。

你可以这样做来检查它是否被处理掉了。当UdpClient被释放时,客户端被设置为null

private void OnUdpData(IAsyncResult result)
{
    if (_udpReceive.Client == null)
        return;
    byte[] data = _udpReceive.EndReceive(result, ref _receiveEndPoint);

    //Snip doing stuff with data

    if (_udpReceive.Client == null)
        return;
    _udpReceive.BeginReceive(OnUdpData, null);
}
尽管因为您是在一个单独的线程中关闭它,您最终可能会遇到竞争条件。最好只捕获ObjectDisposedException和SocketException

private void OnUdpData(IAsyncResult result)
{
    try
    {
        byte[] data = _udpReceive.EndReceive(result, ref _receiveEndPoint);

        //Snip doing stuff with data

        _udpReceive.BeginReceive(OnUdpData, null);
    }
    catch (Exception e)
    {
        //You may also get a SocketException if you close it in a separate thread.
        if (e is ObjectDisposedException || e is SocketException)
        {
            //Log it as a trace here
            return;
        }
        //Wasn't an exception we were looking for so rethrow it.
        throw;
    }
}

这完全是出于设计。您做了一些异常的事情,关闭了套接字,即使您希望收到数据。所以你会得到一个例外。NET framework始终确保异步调用已完成,并且在调用EndXxx()时在回调中发出中止原因的信号。好主意,这样可以清除与回调关联的任何状态


通过等待传输完成,停止调用BeginReceive(),然后关闭套接字,可以使其成为非异常。但这并不总是切实可行的,有时你真的想提前终止合同。没问题,只需捕获ObjectDisposedException并退出即可。当然,一定要考虑到手机另一端的应用程序会发生什么。它随后发送的任何内容都将落入比特桶中,无法发现。

根据您的问题,听起来您希望避免在强制客户端关闭时抛出异常。我将对您的代码进行一些猜测,并尝试提供一个解决方案:

因为您有一个名为“OnUdpData”的方法,所以我假设您在UDPClient周围有一个包装器类。在该包装类中,您可以执行以下操作:设置一个标志,指示您正在关闭客户机,并且在尝试关闭客户机之前不应再使用该客户机。这避免了在调用
EndReceive()
之前检查
\u udprective.Client==null
所导致的争用条件,因为主线程可以在客户端条件检查之后关闭客户端

    private bool _finishedListening = false;

    public void StopListener()
    {
        _finishedListening = true;
        _udpReceive.Close();
    }

    private void OnUdpData(IAsyncResult result)
    {
      if (_finishedListening == true)
        return;
      byte[] data = _udpReceive.EndReceive(result, ref _receiveEndPoint);
      //Snip doing stuff with data
      _udpReceive.BeginReceive(OnUdpData, null);
    }

回调是否解除了调用
EndReceive
的责任?我的印象是,除了一些不占用资源的
IAsyncResult
类型(例如
Control.BeginInvoke
的返回值)之外,应该总是在每次
BeginXX
之后调用
EndXX
。请注意,捕捉到的异常不是
EndReceive
操作失败的标志,而是“成功”
EndReceive
调用中继导致接收尝试的异常的结果。调用EndX不只是为了从BeginX获取数据吗?不管怎样,在这种情况下,返回是好的。EndReceive将检查相同的内容,并无论如何抛出已处理的异常。