¿Cómo verificar si un enchufe está conectado / desconectado en C #?

¿Cómo se puede verificar si un socket de red (System.Net.Sockets.Socket) aún está conectado si el otro host no le envía un paquete cuando se desconecta (por ejemplo, porque se desconectó de manera desagradable)?

Como Paul Turner respondió que Socket.Connected no puede usarse 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 que 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 regresa verdadero si
    • la conexión está cerrada, restablecida, terminada o pendiente (lo que significa que no hay conexión activa)
    • la conexión está activa y hay datos disponibles para leer
  • s.Available devuelve número de bytes disponibles para la lectura
  • si ambos son verdaderos:
    • no hay datos disponibles para leer, por lo que la conexión no está activa

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

  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; */ } 

La propiedad Socket.Connected le dirá si un socket 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 socket ha sido cerrado por sus propias acciones (descartando el socket, llamando a métodos para desconectarse), Socket.Connected devolverá false . Si el socket se ha desconectado por otros medios, la propiedad volverá a ser true hasta que vuelva a intentar enviar o recibir información, momento en el que se SocketException una SocketException u ObjectDisposedException .

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

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.

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

Hice un método de extensión basado en este 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; } } 

La mejor manera es simplemente hacer que su cliente envíe un PING cada X segundos, y que el servidor asum 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.

Siguiendo el consejo de NibblyPig y zendar , se me ocurrió el siguiente código, que funciona en cada prueba 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 
 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; } } ///  /// Sets the keep-alive interval for the socket. ///  /// The socket. /// Time between two keep alive "pings". /// Time between two keep alive "pings" when first one fails. /// If the keep alive infos were succefully modified. 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. Copie la clase SocketExtensions a su proyecto
  2. Llame a SetKeepAlive en su socket – socket.SetKeepAlive (1000, 2);
  3. Agregue un temporizador para verificar la función IsConnected

Como Alexander Logger señaló en la respuesta de zendar , tienes que enviar algo para estar completamente seguro. En caso de que su compañero conectado lea en este socket, puede usar el siguiente código.

 bool SocketConnected(Socket s) { // Exit if socket is null if (s == null) return false; bool part1 = s.Poll(1000, SelectMode.SelectRead); bool part2 = (s.Available == 0); if (part1 && part2) return false; else { try { int sentBytesCount = s.Send(new byte[1], 1, 0); return sentBytesCount == 1; } catch { return false; } } } 

Pero incluso entonces podría tomar unos segundos hasta que se detecte un cable de red roto o algo similar.

Simplemente use KeepAlive como dice @ toster-cx y luego use el estado Socket Connected para verificar si el Socket todavía está conectado. Establezca el tiempo de espera de recepción al mismo tiempo de espera del keepalive. Si tienes más preguntas, siempre me complace ayudarte.

Use Socket.Connected Property.