2010-11-10 32 views
21

Tengo una conexión TCP. El servidor solo lee datos del cliente. Ahora, si se pierde la conexión, el cliente obtendrá un error al escribir los datos en la tubería (tubería rota), pero el servidor aún escucha en esa tubería. ¿Hay alguna manera de que pueda encontrar si la conexión es UP o NO?¿Cómo encontrar el estado de conexión del socket en C?

+2

creo que debería aclarar ya sea que se está desarrollando para windows o Linux/Unix .. – nairdaen

+0

Al enviar los resultados en un error, primero debe comprobar lo que realmente es error y luego decidir si eso es un error recuperable (¿podría solucionarlo por sí solo?) o si significa que su conexión está muerta con seguridad. Y si está muerto seguro, cierre el enchufe. Si aún puede atravesar el tráfico, el otro lado verá que el socket se ha cerrado. Si no puedes atravesar el tráfico, de todas maneras estás perdido. En ese caso, la única forma en que el otro lado puede notar es enviando algo en sí mismo que debe desencadenar una respuesta y si no hay tal respuesta, entonces el otro lado está muerto. – Mecki

Respuesta

30

Se podría llamar getsockopt como la siguiente:

int error = 0; 
socklen_t len = sizeof (error); 
int retval = getsockopt (socket_fd, SOL_SOCKET, SO_ERROR, &error, &len); 

Para probar si la toma es de hasta:

if (retval != 0) { 
    /* there was a problem getting the error code */ 
    fprintf(stderr, "error getting socket error code: %s\n", strerror(retval)); 
    return; 
} 

if (error != 0) { 
    /* socket has a non zero error status */ 
    fprintf(stderr, "socket error: %s\n", strerror(error)); 
} 
+12

Aunque no detectará un socket silenciosamente roto. Un escenario común es que una puerta de enlace NAT intercepta entre la conexión (y nadie recibe notificaciones); para detectar eso, como han dicho otros, deberá enviar algo periódicamente mediante la implementación de algún tipo de latido en su protocolo de aplicación, o al menos use TCP_KEEPALIVE – nos

+0

, cualquiera que se pregunte, creo que 'SOL_SOCKET' significa 'socket option level: socket' y' SO_ERROR' es el último código de error en el socket. – n611x007

+1

Esto solo devuelve el último error. No detecta errores pendientes. – EJP

-1

Para sockets BSD Me echa un vistazo a Beej's guide. Cuando recv devuelve 0, usted sabe que el otro lado está desconectado.

Ahora, es posible que en realidad se pregunte, ¿cuál es la forma más fácil de detectar la desconexión del otro lado? Una forma de hacerlo es tener un hilo siempre haciendo un recv. Ese hilo será capaz de decir instantáneamente cuándo el cliente se desconecta.

+7

no, no lo hará. La pila tcp solo puede detectar que un socket está cerrado si (a) recibe un paquete del otro lado, lo dice, o (b) agota el tiempo de espera de un paquete enviado. En el caso de un recv, si un enrutador intermedio se cae o el host remoto cierra el socket de forma anómala (sin enviar un cierre), nunca llegará ningún paquete para alertar a la pila de zócalos que el receptor está esperando en una conexión de apagado. –

3

La opción de socket TCP keepalive (SO_KEEPALIVE) ayudaría en este escenario y cerraría el socket del servidor en caso de pérdida de conexión.

+2

No cerraría nada. Daría un error en el siguiente envío o recepción, pero el socket permanece abierto. – EJP

10

La única forma de detectar de manera confiable si un socket todavía está conectado es intentar enviar datos periódicamente. Por lo general, es más conveniente definir un paquete de 'ping' a nivel de aplicación que los clientes ignoran, pero si el protocolo ya se ha especificado sin esa capacidad, debe poder configurar los zócalos tcp para ello configurando la opción de socket SO_KEEPALIVE. Me he vinculado a la documentación de winsock, pero la misma funcionalidad debería estar disponible en todas las pilas de socket BSD.

+0

Gracias por su tiempo. Consideré la opción SO_KEEPALIVE, pero el tiempo predeterminado para activar las sondas KeepAlive es de 2 horas en Linux. Si dejo esto en segundos, otras aplicaciones pueden verse afectadas. Le daré una oportunidad de todos modos :) – Blacklabel

+1

Es por eso que es común recurrir a un paquete de ping de nivel de aplicación que el servidor puede enviar cada vez que la conexión ha estado en silencio durante algún intervalo. Si, como en http, no puede enviar datos sin recibir una solicitud del cliente, entonces no tiene otra opción que simplemente cerrar las conexiones que han estado en silencio por "demasiado tiempo" y confiar en que los clientes volverán a conectarse cuando sea necesario. –

+5

@Blacklabel Linux (y otros) le permiten establecer el tiempo de espera de keepalive por socket, consulte la opción de socket TCP_KEEPIDLE en 'man 7 tcp' – nos

2

Tuve un problema similar. Quería saber si el servidor está conectado al cliente o si el cliente está conectado al servidor. En tales circunstancias, el valor de retorno de la función recv puede ser útil. Si el socket no está conectado, devolverá 0 bytes. Utilizando esto rompí el bucle y no tuve que usar ningún hilo adicional de funciones. También puede usar esto si los expertos consideran que este es el método correcto.

1

get sock opt puede ser algo útil, sin embargo, otra forma sería tener un manejador de señales instalado para SIGPIPE. Básicamente cada vez que se rompe la conexión del socket, el kernel enviará una señal SIGPIPE al proceso y luego puede hacer lo necesario. Pero esto aún no proporciona la solución para conocer el estado de la conexión. espero que esto ayude.

1

Debe intentar utilizar: getpeername function.

ahora cuando la conexión está bajada obtendrá en errno: ENOTCONN - La toma no está conectada. lo que significa para ti ABAJO.

else (si no hay otros errores) el código de retorno será 0 -> lo que significa UP.

recursos: página hombre: http://man7.org/linux/man-pages/man2/getpeername.2.html

+1

Nope. ENOTCONN significa que la función 'connect' no ha sido llamada en el socket pasado a la función' getpeername'. El zócalo puede estar "conectado", pero eso no significa que la conexión esté activa. –

+0

Hola, ¿has visto un socket TCP que conectó pero no UP? –

+0

Por "conectado" me refiero a un socket en el que 'connect' se ha llamado con éxito. Y al decir "arriba" quiero decir que la conexión no está medio abierta. Cuando el servidor actúa solo como oyente, puede escuchar literalmente para siempre en un socket conectado, que en realidad no está activo (es decir, está medio abierto). Este artículo explica las conexiones semiabiertas en detalle: http://blog.stephencleary.com/2009/05/detection-of-half-open-dropped.html –

0

puede utilizar SS_ISCONNECTED macro en getsockopt() función. SS_ISCONNECTED se define en socketvar.h.

+2

Parece que el kernel es interno para mí. – EJP

0

Hay una manera fácil de verificar el estado de conexión del socket a través de la llamada poll. En primer lugar, debe sondear el socket, si tiene el evento POLLIN.

  1. Si el socket no está cerrado y hay datos para leer, entonces read devolverá más de cero.
  2. Si no hay datos nuevos sobre el zócalo, a continuación, POLLIN se ajustará a 0 en revents
  3. Si socket está cerrado entonces POLLIN bandera será puesto a uno y leyó volverá 0.

Aquí es pequeño fragmento de código:

int client_socket_1, client_socket_2; 
if ((client_socket_1 = accept(listen_socket, NULL, NULL)) < 0) 
{ 
    perror("Unable to accept s1"); 
    abort(); 
} 
if ((client_socket_2 = accept(listen_socket, NULL, NULL)) < 0) 
{ 
    perror("Unable to accept s2"); 
    abort(); 
} 
pollfd pfd[]={{client_socket_1,POLLIN,0},{client_socket_2,POLLIN,0}}; 
char sock_buf[1024]; 
while (true) 
{ 
    poll(pfd,2,5); 
    if (pfd[0].revents & POLLIN) 
    { 
     int sock_readden = read(client_socket_1, sock_buf, sizeof(sock_buf)); 
     if (sock_readden == 0) 
      break; 
     if (sock_readden > 0) 
      write(client_socket_2, sock_buf, sock_readden); 
    } 
    if (pfd[1].revents & POLLIN) 
    { 
     int sock_readden = read(client_socket_2, sock_buf, sizeof(sock_buf)); 
     if (sock_readden == 0) 
      break; 
     if (sock_readden > 0) 
      write(client_socket_1, sock_buf, sock_readden); 
    } 
} 
+0

Este es un buen comienzo, pero lo que el OP estaba preguntando es sobre errores ... si un socket genera un error, se obtiene un 'POLLERR',' POLLHUP', 'POLLRDHUP', o' POLLNVAL'. Si su 'revent' tiene alguno de esos indicadores establecidos, entonces sabrá que el socket está ahora desconectado y puede hacerlo bien. Además, no debe 'write()' a menos que el socket devuelva 'POLLOUT'. Finalmente, al menos en Linux, es 'POLLIN' (no' _'). Encuentre una versión completamente funcional alrededor de la línea 7000 en https://github.com/m2osw/snapcpp/blob/master/snapwebsites/libsnapwebsites/src/snap_communicator.cpp –

Cuestiones relacionadas