如何中止socket的BeginReceive()?

时间:2011-01-11 20:55:15

标签: c# sockets asynchronous dispose

当然,如果没有数据,BeginReceive()将永远不会结束。 调用Close()的MSDN suggests将中止BeginReceive()

但是,在套接字上调用Close()也会对其执行Dispose(),如this great ansewr所示,因此EndReceive()会抛出异常,因为对象是已经处理好了(确实如此!)。

我该怎么办?

7 个答案:

答案 0 :(得分:49)

这似乎是(非常愚蠢的)设计。您必须抛出此异常并将其捕获到您的代码中。

MSDN确实对它保持沉默,但是如果你看一下另一个异步套接字方法的文档,BeginConnect(),我们发现的是:

  

取消待处理的呼叫   BeginConnect()方法,关闭   插座。当Close()方法是   在异步操作时调用   正在进行中,提供了回调   到BeginConnect()方法是   调用。随后打电话到   EndConnect(IAsyncResult)方法会   抛出一个ObjectDisposedException   表明操作已经完成   取消。

如果这是为BeginConnect做的正确方法,那么BeginReceive也可能如此。这在微软的异步API方面肯定是一个糟糕的设计,因为让用户必须抛出并捕获异常作为正常流程的一部分会使调试器烦恼。在操作完成之前,你真的没有办法“等待”,因为Close()是首先完成它的。

答案 1 :(得分:2)

我很惊讶没有人建议使用SocketOptions。

一旦堆栈具有发送或接收操作,它就会被套接字的套接字选项绑定。

使用较小的发送或接收超时并在操作之前使用它,因此您不必关心在同一操作期间它是否更改为更短或更长的时间。

这将导致更多的上下文切换,但不需要在任何协议下关闭套接字。

例如:

1)设置一个小超时

2)执行操作

3)设置超时

这类似于使用Blocking = false但使用您指定的自动超时。

答案 2 :(得分:0)

你可以在这里阅读我对这个问题的解决方案(在这里使用Pavel Radzivilovsky的评论): UdpClient.ReceiveAsync correct early termination

答案 3 :(得分:0)

对于TCP套接字连接,您可以在尝试访问任何已处置的方法之前使用Connected属性来确定套接字的状态。每个MSDN:

" Connected属性获取上一次I / O操作时Socket的连接状态。当它返回false时,Socket从未连接,或者不再连接。"

因为它说"不再连接"它意味着之前在套接字上调用了Close()。如果在接收回调开始时检查套接字是否已连接,则不会有异常。

答案 4 :(得分:0)

我在ReceiveCallback中检查了client.connect在try块中。 现在,在BeginReceive之后接收到数据时,我可以调用client.Close();。 这样,我看不到异常。我每200毫秒发送一次modbus-TCP请求,并及时获得响应。控制台输出看起来很干净。我使用Windows窗体应用程序对此进行了测试。

    private static void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            // Retrieve the state object and the client socket
            // from the asynchronous state object.  
            StateObject state = (StateObject)ar.AsyncState;
            Socket client = state.workSocket;
            if (client.Connected)
            {
                // Read data from the remote device.  
                state.dataSize = client.EndReceive(ar);
                if (state.dataSize > 0)
                {
                    Console.WriteLine("Received: " + state.dataSize.ToString() + " bytes from server");
                    // There might be more data, so store the data received so far.  
                    state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, state.dataSize));
                    //  Get the rest of the data.  
                    client.BeginReceive(state.buffer, 0, StateObject.BUFFER_SIZE, 0,
                        new AsyncCallback(ReceiveCallback), state);

                    state.dataSizeReceived = true;           //received data size?

                    dataSize = state.dataSize;
                    buffer = state.buffer.ToArray();
                    dataSizeReceived = state.dataSizeReceived;

                    string hex = ByteArrayToString(state.buffer, state.dataSize);
                    Console.WriteLine("<- " + hex);

                    receiveDone.Set();
                    client.Close();
                }
                else
                {
                    Console.WriteLine("All the data has arrived");
                    // All the data has arrived; put it in response.  
                    if (state.sb.Length > 1)
                    {
                        Console.WriteLine("Length: " + state.sb.Length.ToString());
                    }
                    // Signal that all bytes have been received.  
                    receiveDone.Set();
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.ToString());
        }
    }

答案 5 :(得分:-1)

另一种解决方案是使用绑定到不同端口的套​​接字向“自己”发送“控制消息”。这不完全是中止,但它会结束你的异步操作。

答案 6 :(得分:-1)

我也在努力解决这个问题,但据我所知,在调用.BeginReceive()之前使用一个简单的布尔标志也是可行的(因此不需要异常处理)。由于我已经进行了启动/停止处理,因此此修复只需一个if语句(向下滚动到OnReceive()方法的底部)。

if (_running)
{
    _mainSocket.BeginReceive(_data, 0, _data.Length, SocketFlags.None, OnReceive, null);
}                

我是否应该忽略这种方法,请告诉我!