2010-08-21 52 views
5

我工作的一个小的.dll专用于TCP通信,插座ObjectDisposed异常

在我的项目我有一个使用的TcpListener接受传入连接的服务器类。 传入连接存储在字典中,并从那里处理。

每个连接的代码如下所示:

public class Connection : ConnectionBase<Coder.Coder> 
    { 
     public Connection(TcpClient client, Guid id) : base() 
     { 
      Id = id; 
      Client = client; 
     } 

     public void Start() 
     { 
      IsConnected = true;    
      Client.Client.BeginReceive(m_message, 0, m_message.Length, SocketFlags.None, new AsyncCallback(on_data_received), null);    
     } 

     public void Stop() 
     { 
      try 
      { 
       Client.Close(); 
       handle_connection_lost(new ConnectionLostArgs(Id)); 
      } 
      catch 
      { } 
     } 

     public void Send(byte[] data) 
     { 
      try 
      { 
       using (NetworkStream s = Client.GetStream()) 
       { 
        using (BinaryWriter w = new BinaryWriter(s)) 
        { 
         var buffer = m_coder.Encode(data); 
         w.Write(buffer); 
         w.Flush(); 
        } 
       } 

      } 
      catch 
      { handle_connection_lost(new ConnectionLostArgs(Id)); } 
     } 

     public Guid Id { get; set; } 
     public TcpClient Client { get; set; } 

     private byte[] m_message = new byte[1024];   

     private void on_data_received(IAsyncResult ar) 
     { 
      try 
      { 
       Client.Client.BeginReceive(m_message, 0, m_message.Length, 
         SocketFlags.None, new AsyncCallback(on_data_received), null); 

       int bytesRead = Client.Client.EndReceive(ar); 

       if (bytesRead > 0) 
       { 
        byte[] data = new byte[bytesRead]; 
        Array.Copy(m_message, data, bytesRead); 

        m_coder.Push(data); 

       }    
      } 
      catch(Exception ex) 
      { 
       Console.WriteLine("Connection::on_data_received : {0}", ex.Message); 
       handle_connection_lost(new ConnectionLostArgs(Id)); 
      } 
     } 

     protected override void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       try 
       { 
        Stop(); 
       } 
       catch 
       { } 
      } 

      base.Dispose(disposing); 
     } 
    } 

*请注意,编码器负责解码和编码数据包的类。

除了以上的,我有一个基于Socket的TcpClient的(这我希望能与Silverlight的时候重新使用),代码如下:

public class TcpSocketClient : TcpClientBase<Coder.Coder> 
    { 
     public static TcpSocketClient Create(string host, int port) 
     {    
      if (port == 0) 
       return null; 

      return new TcpSocketClient(host, port); 
     } 

     private TcpSocketClient(string host, int port) : base() 
     { 
      IsConnected = false; 
      RemoteEndpoint = new DnsEndPoint(host, port); 
     } 

     public void Start() 
     { 
      m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 

      byte[] buffer = new byte[1024]; 

      SocketAsyncEventArgs e = new SocketAsyncEventArgs() 
      { 
       RemoteEndPoint = RemoteEndpoint, 
       UserToken = m_socket, 

      }; 
      e.SetBuffer(buffer, 0, buffer.Length); 
      e.Completed += new EventHandler<SocketAsyncEventArgs>(handle_socket_connect_completed); 

      m_socket.ConnectAsync(e); 
     }   

     public void Stop() 
     { 
      try 
      { 
       m_socket.Close(); 
       m_socket.Dispose(); 
      } 
      catch (ObjectDisposedException) 
      { } 
     } 

     public void Send(byte[] data) 
     { 
      try 
      { 
       var buffer = m_coder.Encode(data); 
       SocketAsyncEventArgs e = new SocketAsyncEventArgs() 
       { 
        BufferList = new List<ArraySegment<byte>>() { new ArraySegment<byte>(buffer) }, 
        UserToken = m_socket 
       }; 
       m_socket.SendAsync(e);     
      } 
      catch (Exception ex) 
      { handle_client_disconnected(ex.Message); } 
     } 

     #region Properties 
     public DnsEndPoint RemoteEndpoint { get; private set; } 
     #endregion 

     #region Fields 
     Socket m_socket; 
     #endregion 

     void handle_socket_connect_completed(object sender, SocketAsyncEventArgs e) 
     { 
      if (!m_socket.Connected) 
      { 
       handle_client_disconnected("Failed to connect"); 
       return; 
      } 


      e.Completed -= handle_socket_connect_completed; 
      e.Completed += new EventHandler<SocketAsyncEventArgs>(handle_socket_async_receive); 

      handle_client_connected(); 

      m_socket.ReceiveAsync(e);       
     } 

     void handle_socket_async_receive(object sender, SocketAsyncEventArgs e) 
     { 
      if (e.BytesTransferred == 0) 
      { 
       handle_client_disconnected("Connection closed by the remote host"); 
       try { m_socket.Close(); } 
       catch { } 
       return; 
      } 

      try 
      { 
       byte[] buffer = new byte[e.BytesTransferred]; 
       Array.Copy(e.Buffer, buffer, e.BytesTransferred); 
       m_coder.Push(buffer);     
      } 
      catch { } 


      m_socket.ReceiveAsync(e);    
     } 

     protected override void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       try 
       { 
        RemoteEndpoint = null; 
        m_socket.Close(); 
        m_socket.Dispose(); 
       } 
       catch 
       { } 
      } 

      base.Dispose(disposing); 
     } 
    } 

我已经创建了一个集的单元测试。

在其中一个测试中,我将数据从客户端发送到服务器。作品。 在另一个测试中,我将服务器连接的数据发送到客户端。史诗失败。 我一直在Connection的on_data_received中收到Socket ObjectDisposed异常。说实话,我不知道发生了什么 - 因此我需要一些援助。

我使用.NET 4.0,VS 2010,我的机器的操作系统是Win7的(如果这个信息是有帮助)

问候, Maciek

回答

2

我已经想通了 - 最后。

问题看起来很无辜,请检查下面的代码。

public void Send(byte[] data) 
     { 
      try 
      { 
       using (NetworkStream s = Client.GetStream()) 
       { 
        using (BinaryWriter w = new BinaryWriter(s)) 
        { 
         var buffer = m_coder.Encode(data); 
         w.Write(buffer); 
         w.Flush(); 
        } 
       } 

      } 
      catch 
      { handle_connection_lost(new ConnectionLostArgs(Id)); } 
     } 

处置时(感谢using关键字),无论是的BinaryWriter或的NetworkStream插座会得到安置(我不知道这是否是所期望的行为) - 因此中断连接。删除“使用”从句解决了这个问题。

在这里发布答案,以防其他人遇到类似情况。

1

在你on_data_received处理程序中,您呼叫Client.Client.BeginReceive(...)之前Client.Client.EndReceive(...)

BeginReceive可能会同步完成,导致异常并处理您的Connection,所以您应该在EndReceive之后调用它。

private void on_data_received(IAsyncResult ar) 
    { 
     try 
     { 
      int bytesRead = Client.Client.EndReceive(ar); 

      if (bytesRead > 0) 
      { 
       byte[] data = new byte[bytesRead]; 
       Array.Copy(m_message, data, bytesRead); 

       m_coder.Push(data); 

       Client.Client.BeginReceive(m_message, 0, m_message.Length, 
        SocketFlags.None, new AsyncCallback(on_data_received), null); 
      } 
      else 
      { 
       //TODO Close the connection 
      } 

     } 
     catch(Exception ex) 
     { 
      Console.WriteLine("Connection::on_data_received : {0}", ex.Message); 
      handle_connection_lost(new ConnectionLostArgs(Id)); 
     } 
    } 

注意:您应该在接收0字节时关闭连接,这意味着远程端点已关闭。不这样做可能会导致无限循环。

+0

这是一个有效的观点,但它并没有解决主要问题。服务器 - 数据 - >客户端:(仍然在努力完成这项工作 – Maciek 2010-08-21 18:43:55

+0

关于“注意:您应该在接收0字节时关闭连接” – 2015-12-21 12:14:15