2010-08-17 20 views
2

我正在处理客户端/服务器应用程序,其中从客户端到服务器的连接保持打开状态,直到客户端应用程序关闭。当另一端关闭时,套接字未注册

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

我写了一个测试,我认为应该测试,该系统的工作原理,但我的目标测试似乎没有注册,除非我把一个破发点,然后继续关闭套接字。

测试的重要组成部分,是这样的:

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意识到,服务器已经走了吗?

这是可能的回环地址?

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

回答

3

我可以给出一些有关可能有所帮助的一般指示。

环回(或只是使用本地主机)通常不会像真实网络一样行事。例如每次调用socket api时发送/接收多少数据的场景。所以总是测试或真正的网络连接。

套接字API将只发现如果对方是在试图发送给它断开连接(我认为这是正确的)。所以某种心跳功能很方便=)

编辑:你也可以通过尝试接收(对我的一些旧代码做一些基本测试)来确定另一端是否断开连接。

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(); 
     } 
} 

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

+0

感谢您的指点。我会尝试使用不同于回送的地址。 – 2010-08-17 11:24:54

+1

Markus是正确的; [你必须发送数据来检测连接是否仍然可行](http://nitoprograms.blogspot.com/2009/05/detection-of-half-open-dropped.html)。 – 2010-08-17 12:17:56

+0

优秀的链接。谢谢,史蒂芬。我心跳加速,问题消失了。 – 2010-08-17 12:45:32