2010-08-21 9 views
5

Estoy trabajando en un pequeño .dll dedicada a la comunicación TCP,zócalo ObjectDisposed Excepción

En mi proyecto tengo una clase de servidor que utiliza un TcpListener para aceptar conexiones entrantes. Las conexiones entrantes se almacenan en un diccionario y se manejan desde allí.

El código de cada conexión se ve así:

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

* Tenga en cuenta que el codificador es una clase responsable de los paquetes de datos de codificación y decodificación.

Aparte de lo anterior, tengo una TcpClient basada en sockets (que estoy con la esperanza de volver a utilizar más tarde con Silverlight), El código es el siguiente:

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

he creado un conjunto de pruebas unitarias para ambos.

En una de las pruebas envío datos desde el cliente al servidor. Trabajos. En otra prueba, envío datos desde la conexión del servidor a un cliente. Fallo épico. Sigo recibiendo excepciones de objeto de socket en conexión on_data_received. Para ser sincero, no tengo idea de lo que está pasando, por lo tanto, necesito ayuda.

estoy usando .Net 4, VS 2010, el sistema operativo de mi máquina es Win7 (si esta información es de ninguna ayuda)

Saludos, Maciek

Respuesta

2

Lo he descubierto, finalmente.

El problema parecía bastante inocente, examine el siguiente código.

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

Cuando se deshaga (gracias al uso de la palabra clave) o bien el BinaryWriter o la NetworkStream la toma conseguiría dispuesto (no estoy seguro de si este es el comportamiento deseado) - y por lo tanto romper la conexión. Eliminar las cláusulas "usar" resolvió el problema.

Publicando la respuesta aquí en caso de que alguien más se encuentre con algo similar.

1

En el controlador de on_data_received, que está llamando Client.Client.BeginReceive(...) antes de Client.Client.EndReceive(...).

El BeginReceive puede completarse sincrónicamente, causar una excepción y disponer de su Connection, por lo que debe llamarlo después del 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)); 
     } 
    } 

Nota: Debe cerrar su conexión al recibir 0 bytes, esto significa que el punto extremo remoto se ha cerrado. No hacerlo puede causar un ciclo infinito.

+0

Ese es un punto válido, sin embargo, no resolvió el problema principal. Servidor - datos -> Cliente :(Todavía está luchando por hacer esto – Maciek

+0

Buen punto acerca de "Nota: debe cerrar su conexión al recibir 0 bytes" –

Cuestiones relacionadas