¿Cómo encontrar el estado de conexión del socket en C?

7 minutos de lectura

avatar de usuario
etiqueta negra

Tengo una conexión TCP. El servidor solo lee los datos del cliente. Ahora, si se pierde la conexión, el cliente obtendrá un error mientras escribe los datos en la tubería (tubería rota), pero el servidor aún escucha en esa tubería. ¿Hay alguna manera de encontrar si la conexión está ACTIVA o NO?

  • Creo que debería aclarar si está desarrollando para Windows o Linux/Unix.

    – nairdaen

    10 de noviembre de 2010 a las 7:14

  • Cuando el envío da como resultado un error, primero debe verificar qué error es realmente y luego decidir si es un error recuperable (¿podría arreglarse por sí solo?) o si significa que su conexión está muerta con seguridad. Y si está muerto seguro, cierre el zócalo. Si todavía puede pasar algo de tráfico, el otro lado verá que el socket se ha cerrado. Si no puede pasar ningún tráfico, está perdido de todos modos. En ese caso, la única forma en que el otro lado puede darse cuenta es enviando algo por sí mismo que debería desencadenar una respuesta y, si no hay tal respuesta, entonces el otro lado está muerto.

    – Mecki

    18 de julio de 2017 a las 15:57

avatar de usuario
simone

Podría llamar a getsockopt de la siguiente manera:

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

Para probar si el enchufe está arriba:

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

  • Sin embargo, no detectará un enchufe roto silenciosamente. Un escenario común es que una puerta de enlace NAT entre el tiempo de espera de la conexión (y nadie recibe una notificación); para detectar eso, como han dicho otros, tendrá que enviar algo periódicamente implementando algún tipo de latidos en su protocolo de aplicación, o al menos use TCP_KEEPALIVE

    – nos

    10 de noviembre de 2010 a las 8:15

  • cualquiera que se pregunte, creo SOL_SOCKET significa ‘nivel de opción de socket: socket’ y SO_ERROR es el último código de error en el socket.

    – n611x007

    30 de agosto de 2013 a las 12:05

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

    – usuario207421

    20 de diciembre de 2015 a las 6:40

  • ofc no detecta errores que aún no han ocurrido, no es una máquina del tiempo

    – hanshenrik

    5 de marzo de 2016 a las 12:33

  • strerror(retval) parece incorrecto, al menos en Linux. La página del manual para getsockopt dice “En caso de éxito, se devuelve cero. En caso de error, se devuelve -1 y errno se establece de manera adecuada”. Entonces debería ser strerror (errno)

    – Mijaíl Kagalenko

    28 de noviembre de 2016 a las 13:45


avatar de usuario
chris becke

La única forma de detectar de manera confiable si un enchufe aún está conectado es intentar enviar datos periódicamente. Por lo general, es más conveniente definir un paquete ‘ping’ de nivel de aplicación que los clientes ignoren, pero si el protocolo ya está especificado sin tal capacidad, debería poder configurar los sockets tcp para hacer esto configurando el SO_KEEPALIVE opción de enchufe. Me he vinculado a la documentación de winsock, pero la misma funcionalidad debería estar disponible en todas las pilas de sockets similares a BSD.

  • Gracias por tu tiempo. Consideré la opción SO_KEEPALIVE, pero el tiempo predeterminado para activar las sondas KeepAlive es de 2 horas en Linux. Si reduzco esto a segundos, otras aplicaciones podrían verse afectadas. Le daré una oportunidad de todos modos 🙂

    – Etiqueta negra

    10 de noviembre de 2010 a las 8:44

  • Esta es la razón por la que es común recurrir a un paquete de ping a nivel de aplicación que el servidor puede enviar cada vez que la conexión ha estado inactiva 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 durante “demasiado tiempo” y confiar en que los clientes se vuelvan a conectar cuando sea necesario.

    – Chris Becke

    10 de noviembre de 2010 a las 9:41

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

    – nos

    10 de noviembre de 2010 a las 20:12

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.

  • No cerraría nada. Daría un error en el próximo envío o recepción, pero el socket permanece abierto.

    – usuario207421

    20 de diciembre de 2015 a las 6:34

avatar de usuario
yanpas

Hay una manera fácil de verificar el estado de la conexión del socket a través de poll llamada. Primero, debe sondear el socket, ya sea que tenga POLLIN evento.

  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 en el socket, entonces POLLIN se establecerá en 0 en revents
  3. Si el enchufe está cerrado, entonces POLLIN la bandera se establecerá en uno y la lectura devolverá 0.

Aquí hay un 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);
    }
}

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

  • Si la conexión tiene ha fallado no devolverá 0, se bloqueará para siempre, o hasta que transcurra el tiempo de espera de lectura, si lo hay. 0 significa una desconexión ordenada, no un tirón de cable, por ejemplo.

    – usuario207421

    6 de marzo de 2020 a las 7:43

avatar de usuario
bem22

Muy simple, como se muestra en la imagen. recibir.

Para verificar que querrá leer 1 byte del socket con MSG_PEEK y MSG_DONT_WAIT. Esto no eliminará los datos (PEEK) y la operación es sin bloqueo (DONT_WAIT)

while (recv(client->socket,NULL,1, MSG_PEEK | MSG_DONTWAIT) != 0) {
    sleep(rand() % 2); // Sleep for a bit to avoid spam
    fflush(stdin);
    printf("I am alive: %d\n", socket);
}

// When the client has disconnected, this line will execute
printf("Client %d went away :(\n", client->socket);

encontre el ejemplo aquí.

  • Si la conexión tiene ha fallado no devolverá 0, se bloqueará para siempre, o hasta que transcurra el tiempo de espera de lectura, si lo hay. 0 significa una desconexión ordenada, no un tirón de cable, por ejemplo.

    – usuario207421

    6 de marzo de 2020 a las 7:43

avatar de usuario
chandank

get sock opt puede ser algo útil, sin embargo, otra forma sería tener un controlador de señal 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 podrá hacer lo necesario. Pero esto todavía no proporciona la solución para conocer el estado de la conexión. espero que esto ayude.

¿Ha sido útil esta solución?