2011-11-17 11 views
6

Recientemente he abordado un comportamiento extraño del método .Net synchronous receive. Necesitaba escribir una aplicación que tiene nodos que se comunican entre sí enviando/recibiendo datos. Cada servidor tiene un bucle de recibo que es sincrónico, después de recibir una clase serializada, lo deserializa y lo procesa. Después de eso envía asincrónicamente esta clase serializada a algunos nodos elegidos (usando AsynchSendTo)..NET C# La recepción sincrónica no bloquea

El MSDN dice claramente que:

"Si está utilizando un conector orientado a conexión, el método Recibe va a leer todos los datos que está disponible, hasta el tamaño de la memoria intermedia Si. el host remoto apaga la conexión de Socket con el método Shutdown , y se han recibido todos los datos disponibles, el método Receive se completará inmediatamente y devolverá cero bytes ".

En mi caso, no es cierto. Hay algunos casos aleatorios cuando Receive no bloquea y devuelve 0 bytes (situtation no determinista) inmediatamente después de establecer la conexión. Estoy 100% seguro de que el remitente estaba enviando por lo menos 1000 bytes. Un hecho más curioso: al poner Sleep (500) antes de recibir todo funciona bien. A continuación se presenta el código de recepción:

_listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
try 
{ 
    _listener.Bind(_serverEndpoint); 
    _listener.Listen(Int32.MaxValue); 
    while (true) 
    { 
     Console.WriteLine("Waiting for connection..."); 
     Socket handler = _listener.Accept(); 

     int totalBytes = 0; 
     int bytesRec; 
     var bytes = new byte[DATAGRAM_BUFFER]; 
     do 
     { 
      //Thread.Sleep(500); 
      bytesRec = handler.Receive(bytes, totalBytes, handler.Available, SocketFlags.None); 
      totalBytes += bytesRec; 
     } while (bytesRec > 0); 

     handler.Shutdown(SocketShutdown.Both); 
     handler.Close(); 
    } 
} 
catch (SocketException e) 
{ 
    Console.WriteLine(e); 
} 

también la parte que envía:

public void AsynchSendTo(Datagram datagram, IPEndPoint recipient) 
{ 

    byte[] byteDatagram = SerializeDatagram(datagram); 
    try 
    { 
     var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     socket.BeginConnect(recipient, ConnectCallback, new StateObject(byteDatagram, byteDatagram.Length, socket)); 
    } 
    catch (SocketException e) 
    { 
     Console.WriteLine(e); 
    } 
} 

public void ConnectCallback(IAsyncResult result) 
{ 
    try 
    { 
     var stateObject = (StateObject)result.AsyncState; 
     var socket = stateObject.Socket; 
     socket.EndConnect(result); 
     socket.BeginSend(stateObject.Data, 0, stateObject.Data.Length, 0, new AsyncCallback(SendCallback), socket); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine("catched!" + ex.ToString()); 
    } 
} 

public void SendCallback(IAsyncResult result) 
{ 
    try 
    { 
     var client = (Socket)result.AsyncState; 
     client.EndSend(result); 
     client.Shutdown(SocketShutdown.Both); 
     client.Close(); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex); 
    } 
} 

class StateObject 
{ 
    public Byte[] Data { get; set; } 
    public int Size; 
    public Socket Socket; 
} 

Mi pregunta: estoy usando el sincrónica reciben de una manera incorrecta? ¿Por qué no bloquea el evento aunque haya datos para recibir?

+0

Dado que su pregunta no es específicamente sobre el problema que está teniendo, se resuelve con un descanso antes de la lectura. Desde medio segundo cada bucle puede realmente pasar factura con latencia si se lee mucho de una vez, ¿ha intentado dormir después de la lectura con un tiempo de espera más pequeño, como 100 mseg? Esto me ha funcionado al leer los puertos seriales, pero también fue cuando también se produjo un evento de recepción de datos. – jlafay

+0

Esto simplemente no es la forma en que Socket funciona. Comience a diagnosticar esto quitando todos los bloques try/catch. –

Respuesta

4

Te disparas en el pie.

bytesRec = handler.Receive(bytes, totalBytes, handler.Available, SocketFlags.None); 

En el comienzo de la conexión, Available será 0, obligándolo a regresar inmediatamente con 0. En su lugar, usted debe especificar el número de bytes que están libres en su memoria intermedia (por ejemplo bytes.Length-totalBytes), entonces también bloqueará.

6

Puede tener un problema de simultaneidad aquí. Después de aceptar una conexión, saltas directamente a recibir. Es posible que el proceso del remitente no tenga tiempo suficiente para recibir la llamada y, por lo tanto, su handler.Available es 0 y la recepción devuelve.

Esta es también la razón por la cual el "error" no ocurre cuando se agrega el reposo de 500 ms.

+0

Tiene razón, pero 7 minutos tarde;) – Lucero

+0

Creo que también agregué la razón por la que 500 ms lo corrige. No copié tu respuesta ... – Tudor

+0

Lo siento, mi intención era * no * hacer que tu respuesta pareciera como si hubiera sido copiada de la mía. Sin embargo, esencialmente dicen lo mismo: el mío aborda implícitamente el comportamiento diferente con el retraso con la frase "Al principio ...". ;) – Lucero

Cuestiones relacionadas