5

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

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

    //Snip doing stuff with data

    _udpReceive.BeginReceive(OnUdpData, null);
}

当我Close()在主线程中使用 UdpClient 时,回调会按我的预期触发,但此时_udpReceive已经处理完毕,ObjectDisposedException当我尝试调用EndReceive(). 我期待得到一个空缓冲区。

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

4

3 回答 3

5

您可以这样做以检查其是否已处置。当 UdpClient 被释放时,Client 被设置为 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;
    }
}
于 2012-02-16T11:42:38.717 回答
4

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

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

于 2012-02-16T12:31:52.043 回答
0

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

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

    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);
    }
于 2020-06-30T17:23:48.933 回答