2010-08-21 60 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