2010-04-18 57 views

Respuesta

75

Como Paul Turner responde Socket.Connected no se puede utilizar en esta situación. Necesita sondear la conexión cada vez para ver si la conexión aún está activa. Este es el código utilicé:

bool SocketConnected(Socket s) 
{ 
    bool part1 = s.Poll(1000, SelectMode.SelectRead); 
    bool part2 = (s.Available == 0); 
    if (part1 && part2) 
     return false; 
    else 
     return true; 
} 

Funciona así:

  • s.Poll devuelve verdadero si
    • conexión se cierra, reset, terminado o en espera (es decir, sin conexión activa)
    • conexión está activa y no hay datos disponibles para la lectura
  • s.Available rendimientos número de bytes disponibles para su lectura
  • si ambas son verdaderas:
    • no hay datos disponibles para leer así que la conexión no está activa
+0

Creo que no necesita verificar la propiedad 'Disponible'. De acuerdo con el método 'Poll' [página MSDN] (http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.poll.aspx), también devuelve true ** si los datos son disponible para leer **. –

+3

Lea de nuevo la documentación, también devuelve verdadero si la conexión está cerrada o pendiente, por lo que no sabrá si está activa, a menos que compruebe si el otro lado le envió algunos datos. – zendar

+3

Como una línea: 'return! (Socket.Poll (1, SelectMode.SelectRead) && socket.Available == 0)' – kasperhj

-11

Uso Socket.Connected Propiedad.

+7

Ten en cuenta que incluso si Socket.Connected devuelve verdadero, el enchufe no esté conectado. Si es falso, definitivamente no está conectado. – nos

12

La propiedad Socket.Connected le dirá si un conector piensa que está conectado. En realidad, refleja el estado de la última operación de envío/recepción realizada en el socket.

Si el zócalo se ha cerrado por sus propias acciones (desechando el zócalo, llamando a métodos para desconectarlo), Socket.Connected devolverá false. Si el socket se ha desconectado por otros medios, la propiedad devolverá true hasta que intente enviar o recibir información, en cuyo momento se lanzarán SocketException o ObjectDisposedException.

Puede verificar la propiedad después de que se haya producido la excepción, pero antes no era confiable.

1

La mejor es simplemente hacer que su cliente envíe un PING cada X segundos, y que el servidor asuma que está desconectado después de no haber recibido uno por un tiempo.

Me encontré con el mismo problema que cuando usaba sockets, y esta era la única forma en que podía hacerlo. La propiedad socket.connected nunca fue correcta.

Al final, cambié a usar WCF porque era mucho más confiable que los sockets.

21

Como escribió zendar, es bueno usar Socket.Poll y Socket.Available, pero debe tener en cuenta que es posible que el socket no se haya inicializado en primer lugar. Esta es la última (creo) información y la proporciona la propiedad Socket.Connected.La versión revisada del método haría es como la siguiente:

static bool IsSocketConnected(Socket s) 
    { 
     return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected); 

/* The long, but simpler-to-understand version: 

     bool part1 = s.Poll(1000, SelectMode.SelectRead); 
     bool part2 = (s.Available == 0); 
     if ((part1 && part2) || !s.Connected) 
      return false; 
     else 
      return true; 

*/ 
    } 
3

hice un método de extensión basado en this artículo de MSDN. Así es como puede determinar si un socket todavía está conectado.

public static bool IsConnected(this Socket client) 
{ 
    bool blockingState = client.Blocking; 

    try 
    { 
     byte[] tmp = new byte[1]; 

     client.Blocking = false; 
     client.Send(tmp, 0, 0); 
     return true; 
    } 
    catch (SocketException e) 
    { 
     // 10035 == WSAEWOULDBLOCK 
     if (e.NativeErrorCode.Equals(10035)) 
     { 
      return true; 
     } 
     else 
     { 
      return false; 
     } 
    } 
    finally 
    { 
     client.Blocking = blockingState; 
    } 
} 
1

Siguiendo el consejo de NibblyPig y zendar, me ocurrió con el código de abajo, que funciona en todas las pruebas que hice. Terminé necesitando tanto el ping como la encuesta. El ping me avisará si el cable se ha desconectado o si la capa física se ha interrumpido (enrutador apagado, etc.). Pero a veces, después de reconectarme obtengo un RST, el ping está bien, pero el estado tcp no.

#region CHECKS THE SOCKET'S HEALTH 
    if (_tcpClient.Client.Connected) 
    { 
      //Do a ping test to see if the server is reachable 
      try 
      { 
       Ping pingTest = new Ping() 
       PingReply reply = pingTest.Send(ServeripAddress); 
       if (reply.Status != IPStatus.Success) ConnectionState = false; 
      } catch (PingException) { ConnectionState = false; } 

      //See if the tcp state is ok 
      if (_tcpClient.Client.Poll(5000, SelectMode.SelectRead) && (_tcpClient.Client.Available == 0)) 
      { 
       ConnectionState = false; 
      } 
     } 
    } 
    else { ConnectionState = false; } 
#endregion 
4

La respuesta aceptada no parece funcionar si desenchufa el cable de red. O el servidor se cuelga. O su enrutador se bloquea. O si olvida pagar su factura de internet. Establezca las opciones TCP keep-alive para una mejor confiabilidad.

public static class SocketExtensions 
{ 
    public static void SetSocketKeepAliveValues(this Socket instance, int KeepAliveTime, int KeepAliveInterval) 
    { 
     //KeepAliveTime: default value is 2hr 
     //KeepAliveInterval: default value is 1s and Detect 5 times 

     //the native structure 
     //struct tcp_keepalive { 
     //ULONG onoff; 
     //ULONG keepalivetime; 
     //ULONG keepaliveinterval; 
     //}; 

     int size = Marshal.SizeOf(new uint()); 
     byte[] inOptionValues = new byte[size * 3]; // 4 * 3 = 12 
     bool OnOff = true; 

     BitConverter.GetBytes((uint)(OnOff ? 1 : 0)).CopyTo(inOptionValues, 0); 
     BitConverter.GetBytes((uint)KeepAliveTime).CopyTo(inOptionValues, size); 
     BitConverter.GetBytes((uint)KeepAliveInterval).CopyTo(inOptionValues, size * 2); 

     instance.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null); 
    } 
} 



// ... 
Socket sock; 
sock.SetSocketKeepAliveValues(2000, 1000); 

El valor de tiempo establece el tiempo de espera desde que se enviaron los datos por última vez. Luego intenta enviar y recibir un paquete de mantenimiento de vida. Si falla, vuelve a intentar 10 veces (número codificado desde Vista AFAIK) en el intervalo especificado antes de decidir que la conexión está muerta.

De modo que los valores anteriores darían como resultado 2 + 10 * 1 = 12 segundos de detección. Después de eso, cualquier operación de lectura/lectura/encuesta fallará en el socket.

+0

Desconecto el socket por la fuerza (desde una herramienta como el puerto curr), pero no hubo reconexión automática. – Eitan

+1

No se reconectará automáticamente. Es solo una "mejor" forma de detectar cuándo cae una conexión sin enviar datos. –

1
public static class SocketExtensions 
{ 
    private const int BytesPerLong = 4; // 32/8 
    private const int BitsPerByte = 8; 

    public static bool IsConnected(this Socket socket) 
    { 
     try 
     { 
      return !(socket.Poll(1000, SelectMode.SelectRead) && socket.Available == 0); 
     } 
     catch (SocketException) 
     { 
      return false; 
     } 
    } 


    /// <summary> 
    /// Sets the keep-alive interval for the socket. 
    /// </summary> 
    /// <param name="socket">The socket.</param> 
    /// <param name="time">Time between two keep alive "pings".</param> 
    /// <param name="interval">Time between two keep alive "pings" when first one fails.</param> 
    /// <returns>If the keep alive infos were succefully modified.</returns> 
    public static bool SetKeepAlive(this Socket socket, ulong time, ulong interval) 
    { 
     try 
     { 
      // Array to hold input values. 
      var input = new[] 
      { 
       (time == 0 || interval == 0) ? 0UL : 1UL, // on or off 
       time, 
       interval 
      }; 

      // Pack input into byte struct. 
      byte[] inValue = new byte[3 * BytesPerLong]; 
      for (int i = 0; i < input.Length; i++) 
      { 
       inValue[i * BytesPerLong + 3] = (byte)(input[i] >> ((BytesPerLong - 1) * BitsPerByte) & 0xff); 
       inValue[i * BytesPerLong + 2] = (byte)(input[i] >> ((BytesPerLong - 2) * BitsPerByte) & 0xff); 
       inValue[i * BytesPerLong + 1] = (byte)(input[i] >> ((BytesPerLong - 3) * BitsPerByte) & 0xff); 
       inValue[i * BytesPerLong + 0] = (byte)(input[i] >> ((BytesPerLong - 4) * BitsPerByte) & 0xff); 
      } 

      // Create bytestruct for result (bytes pending on server socket). 
      byte[] outValue = BitConverter.GetBytes(0); 

      // Write SIO_VALS to Socket IOControl. 
      socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); 
      socket.IOControl(IOControlCode.KeepAliveValues, inValue, outValue); 
     } 
     catch (SocketException) 
     { 
      return false; 
     } 

     return true; 
    } 
} 
  1. Copiar la clase SocketExtensions a su proyecto
  2. Llame al SetKeepAlive en su zócalo - socket.SetKeepAlive (1000, 2);
  3. Añadir un temporizador para controlar el funcionamiento isConnected
Cuestiones relacionadas