2010-08-21 4 views
5

Ich bin auf einem kleinen DLL zu TCP-Kommunikation gewidmet arbeiten,Sockel ObjectDisposed Exception

In meinem Projekt habe ich eine Serverklasse bekam, die eine TcpListener verwendet eingehende Verbindungen zu akzeptieren. Eingehende Verbindungen werden in einem Dictionary gespeichert und von dort bearbeitet.

jeden Code der Verbindung wie folgt aussieht:

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

* Bitte beachten Sie, dass der Coder ist eine Klasse verantwortlich für die Decodierung und Codierung von Datenpaketen.

Abgesehen von den oben, ich habe eine socket-basierte TcpClient (was ich hoffe, später mit Silverlight wiederzuverwenden) Der Code wird wie folgt dar:

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

Ich habe eine Reihe erstellt Unit-Tests für beide.

In einem der Tests sende ich Daten vom Client an den Server. Funktioniert. In einem anderen Test sende ich Daten von der Verbindung des Servers zu einem Client. Episches Versagen. Ich bekomme Socket ObjectDisposed Ausnahmen in Connections on_data_received. Um ehrlich zu sein, ich habe keine Ahnung, was los ist - deshalb brauche ich etwas Hilfe.

Ich bin mit .NET 4, VS 2010 OS Meine Maschine ist Win7 (wenn diese Informationen von irgendeiner Hilfe ist)

Grüße, Maciek

Antwort

2

Ich habe es herausgefunden - endlich.

Das Problem sah ziemlich unschuldig aus, untersuchen Sie den folgenden Code.

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

Bei der Entsorgung (thx an dem Verwendung von Keyword) entweder die Binary oder das Network der Sockel bekommt entsorgt würde (ich bin nicht sicher, ob dies das gewünschte Verhalten ist) - und damit die Verbindung unterbrechen. Durch das Entfernen der "using" -Klauseln wurde das Problem behoben.

Veröffentlichen Sie die Antwort hier für den Fall, dass jemand in etwas Ähnliches läuft.

1

In Ihrem on_data_received Handler, die Sie anrufen Client.Client.BeginReceive(...) vor Client.Client.EndReceive(...).

Der BeginReceive kann synchron abgeschlossen werden, verursachen eine Ausnahme und entsorgen Sie Ihre Connection, also sollten Sie es nach dem EndReceive aufrufen.

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

Hinweis: Sie sollten Ihre Verbindung schließen, wenn Sie 0 Byte empfangen, dh der Remote-Endpunkt wurde geschlossen. Andernfalls kann dies zu einer Endlosschleife führen.

+0

Das ist ein gültiger Punkt, aber es hat das Hauptproblem nicht gelöst. Server - Daten -> Client :(Immer noch kämpfen, um dies zu tun – Maciek

+0

Guter Punkt über "Hinweis: Sie sollten Ihre Verbindung schließen, wenn Sie 0 Bytes empfangen" –