2010-08-21 28 views
5

Tôi đang làm việc trên một nhỏ .dll dành riêng cho giao tiếp Tcp,Socket ObjectDisposed Exception

Trong dự án của tôi, tôi có một lớp máy chủ sử dụng TcpListener để chấp nhận các kết nối đến. Kết nối đến được lưu trữ trong Từ điển và được xử lý từ đó.

đang Mỗi kết nối trông giống như:

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

* Xin lưu ý rằng Coder là một lớp chịu trách nhiệm giải mã và mã hóa các gói dữ liệu.

Ngoài việc trên, tôi đã có một TcpClient ổ cắm-based (mà tôi hy vọng để tái sử dụng sau với Silverlight), mã này như sau:

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

tôi đã tạo ra một bộ kiểm tra đơn vị cho cả hai.

Trong một trong các thử nghiệm, tôi gửi dữ liệu từ máy khách đến máy chủ. Công trinh. Trong một thử nghiệm khác, tôi gửi dữ liệu từ kết nối của máy chủ đến máy khách. Epic thất bại. Tôi tiếp tục nhận được các ngoại lệ Socket ObjectDisposed trong On_data_received của Connection. Thành thật mà nói tôi không biết chuyện gì đang diễn ra - vì vậy tôi cần một chút trợ giúp.

Tôi đang sử dụng Net 4, VS 2010, hệ điều hành máy của tôi là Win7 (nếu thông tin này là của bất kỳ sự giúp đỡ)

Kính trọng, Maciek

Trả lời

2

Tôi đã tìm ra - cuối cùng.

Vấn đề trông khá ngây thơ, kiểm tra mã bên dưới.

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

Khi xử lý (thx đến từ khóa sử dụng) hoặc là BinaryWriter hoặc NetworkStream socket sẽ được xử lý (Tôi không chắc chắn nếu điều này là hành vi mong muốn) - và do đó phá vỡ các kết nối. Việc loại bỏ các mệnh đề "sử dụng" đã giải quyết được vấn đề.

Đăng câu trả lời ở đây trong trường hợp bất kỳ ai khác gặp phải vấn đề tương tự.

1

Trong handler on_data_received của bạn, bạn đang gọi Client.Client.BeginReceive(...) trước Client.Client.EndReceive(...).

BeginReceive có thể hoàn thành đồng bộ, gây ra một ngoại lệ và xử lý Connection của bạn, vì vậy bạn nên gọi nó sau 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)); 
     } 
    } 

Lưu ý: Bạn nên đóng kết nối khi nhận 0 byte, điều này có nghĩa là điểm cuối từ xa đã đóng. Không làm như vậy có thể gây ra vòng lặp vô hạn.

+0

Đó là điểm hợp lệ, tuy nhiên nó không giải quyết được vấn đề chính. Máy chủ - dữ liệu -> Khách hàng :(Vẫn đang vật lộn để thực hiện việc này – Maciek

+0

Điểm tốt về "Lưu ý: Bạn nên đóng kết nối khi nhận được 0 byte" –