当另一端关闭时,套接字不会注册

时间:2010-08-17 10:47:59

标签: c# unit-testing tcpclient

我正在开发一个客户端/服务器应用程序,其中从客户端到服务器的连接保持打开状态,直到客户端应用程序关闭。

如果服务器应用程序意外关闭,而客户端正在读取数据,我希望客户端将此视为异常,但随后捕获异常并引发异常作为参数的事件。

我已经编写了一个测试,我认为应该测试这个系统是否正常工作,但我正在测试的对象似乎没有注册套接字是关闭的,除非我放入一个断点然后继续。

测试的重要部分如下:

StreamingMonitor sm = new StreamingMonitor();
bool errored = false;
string msg = "";
sm.ErrorOccurred += (s, a) =>
{
    errored = true;
    msg = a.Exception.Message;
};
sm.Enabled = true;
client = listener.AcceptTcpClient();
client.GetStream().Write(BitConverter.GetBytes(10000), 0, 4);
client.Close();
while(!errored)
{}
Assert.AreEqual("A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call", msg);

TcpListener对象listener正在侦听环回地址。

StreamingMonitor开始侦听启用时要检索的数据长度。始终假设数据长度适合带符号的32位整数。

当收到消息长度时,将调用此方法。

    private void GotMessageLength(IAsyncResult asyncResult)
    {
        try
        {
            client.Client.EndReceive(asyncResult);
            if(firstMessage)
            {
                firstMessage = false;
                if (Connected != null)
                {
                    Connected(this, new EventArgs());
                }
            }
            int msgLen = BitConverter.ToInt32(messageLength, 0);
            byte[] message = new byte[msgLen];
            List<byte> lbMessage = new List<byte>();
            int bytesReturned = client.Client.Receive(message);
            int remaining = (msgLen < bytesReturned) ? bytesReturned - msgLen : msgLen - bytesReturned;
            if(remaining > 0)
            {
                if (bytesReturned > 0)
                {
                    for (int i = 0; i < bytesReturned; i++)
                    {
                        lbMessage.Add(message[i]);
                    }
                }
                while(remaining > 0)
                {
                    if(!client.Connected)
                    {
                        throw new SocketException((int)SocketError.Shutdown);
                    }
                    bytesReturned = client.Client.Receive(message);
                    remaining = (remaining < bytesReturned) ? bytesReturned - remaining : remaining - bytesReturned;
                    if (bytesReturned > 0)
                    {
                        for (int i = 0; i < bytesReturned; i++)
                        {
                            lbMessage.Add(message[i]);
                        }
                    }
                }
                message = lbMessage.ToArray();
            }
            MessageReceived(this, new MessageReceivedEventArgs(message));
            if (Enabled)
            {
                client.Client.BeginReceive(messageLength, 0, 4, SocketFlags.None, GotMessageLength, null);
            }
        }
        catch (SocketException ex)
        {
            if(ErrorOccurred != null)
            {
                ErrorOccurred(this, new ErrorEventArgs(ex));
            }
        }
        catch (ObjectDisposedException)
        {

        }
    }

该方法从网络流中读取数据,直到读取指定的字节数。如果远程连接关闭,那么它应该引发套接字异常。

然而,单元测试陷入无限循环,等待错误发生,因为StreamingMonitor中的套接字从未意识到另一端已关闭。

如何让SteamingMonitor意识到服务器已经消失?

这是否可以在环回地址上使用?

对不起所有的代码,我想不出如何削减方法。

1 个答案:

答案 0 :(得分:3)

我可以就可能有用的区域提供一些一般性的指示。

环回(或仅使用localhost)通常与真实网络的行为方式不同。在每次调用socket api时发送/接收多少数据的场景。所以总是测试或真正的网络连接。

套接字api只会在尝试发送给它时发现对方是否断开连接(我认为这是正确的)。所以某种心跳功能派上用场了=)

编辑:您还可以通过尝试接收来确定另一方是否已断开连接以获取SocketException(对我的一些旧代码进行了一些基本测试)。

protected void ReceiveCallback(IAsyncResult ar)
{
     var so = (StateObject)ar.AsyncState;
     if (!so.Socket.Connected) return;

     try
     {
         int read = so.Socket.EndReceive(ar);
         if (read > 0)
         ProcessBuffer(so, so.Buffer, read);

         so.Socket.BeginReceive(so.Buffer, 0, so.Buffer.Length, SocketFlags.None, ReceiveCallback, so);
      }
      catch (SocketException e)
      {
          Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: SocketException");
          Output.WriteLine(e.Message);
          RaiseDisconnected();
      }
      catch (ObjectDisposedException e)
      {
          Trace.WriteLine("[Networking]::NetBase.ReceiveCallback: ObjectDisposedException");
          Output.WriteLine(e.Message);
          RaiseDisconnected();
       }
}

如果对方由于某种原因崩溃,这将调用我的断开连接功能。 希望它有所帮助