2010-12-03 34 views
14

Estoy conectando un proceso de servidor y un proceso de cliente con una conexión TCP, y tengo que detectar
que la conexión física entre las dos máquinas está inactiva. Estoy tratando de hacer esto mediante el mantenimiento de conexión,
disminución de los valores de todo el sistema por defecto a:socket, detectar la conexión se pierde

TCP_KEEPIDLE = 5
TCP_KEEPCNT = 5
TCP_KEEPINTVL = 1

Cuando la conexión se cae (desconecto el cable) solo el servidor en 10 segundos detecta que la conexión se ha perdido, el cliente simplemente cuelga en el envío.

Este es el código de cliente:

#include <iostream> 
#include <string.h> 
#include <sys/socket.h> 
#include <stdlib.h> 
#include <arpa/inet.h> 
#include <errno.h> 
#include <netinet/tcp.h> 

int main(int argc, char** argv) { 
    char myVector[1600]; 

    int mySocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); 
    if (mySocket < 0) { 
    std::cout << "error creating the socket" << strerror(errno) << std::endl; 
    ::exit(-1); 
} 

struct sockaddr_in sin; 
memset((char *)&sin, 0, sizeof(sin)); 
sin.sin_addr.s_addr = inet_addr("192.168.21.27"); 
sin.sin_port = htons(7788); 
sin.sin_family = AF_INET; 

if (connect(mySocket, (struct sockaddr *)&sin, sizeof(sin)) < 0) { 
    std::cout << "Error on connection: " << strerror(errno) << std::endl; 
    ::exit(-1); 
} 

int optval = 1; 
socklen_t optlen = sizeof(optval); 

/*Enabling keep alive*/ 
if(setsockopt(mySocket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { 
    std::cout << "Error setting SO_KEEPALIVE: " << strerror(errno) << std::endl; 
} 

optval = 5; 
optlen = sizeof(optval); 
if(setsockopt(mySocket, SOL_TCP, TCP_KEEPIDLE, &optval, optlen) < 0) { 
    std::cout << "Error setting TCP_KEEPIDLE: " << strerror(errno) << std::endl; 
} 

optval = 5; 
optlen = sizeof(optval); 
if(setsockopt(mySocket, SOL_TCP, TCP_KEEPCNT, &optval, optlen) < 0) { 
    std::cout << "Error setting TCP_KEEPCNT: " << strerror(errno) << std::endl; 
} 

optval = 1; 
optlen = sizeof(optval); 
if(setsockopt(mySocket, SOL_TCP, TCP_KEEPINTVL, &optval, optlen) < 0) { 
    std::cout << "Error setting TCP_KEEPINTVL: " << strerror(errno) << std::endl; 
} 

for (;;) { 
    ssize_t myRet= ::send(mySocket, 
             myVector, 
             sizeof(myVector), 
            0); 
    if (myRet < 0) { 
    std::cout << "Error: " << strerror(errno) << std::endl; 
    break; 
    } 
    std::cout << myRet << "."; std::cout.flush(); 
    sleep(1); 
} 
} 

Estoy seguro de que me falta algo, pero ¿qué?

Respuesta

8

TCP Keepalive no está destinado para este uso.

Si desea detectar interrupciones en la capa de la aplicación, haga lo que hacen los protocolos como SSH, IMAP e IRC: implemente un mensaje de tipo eco/ping en la capa de la aplicación. Envíelos regularmente, y si no recibe una respuesta oportuna, se puede asumir que la conexión está inactiva.

+0

Hm. ¿Pero para qué está destinado? – vines

+1

@vines: permite que las conexiones obsoletas de larga duración sean * eventualmente * detectadas y eliminadas, casos en los que la puntualidad no es importante. – caf

3

Nos preguntamos acerca de esa pregunta en nuestra empresa hace un tiempo: "¿cómo detectar que la conexión se redujo?". Para abordar este problema de manera confiable, tuvimos que implementar un sistema de "ritmo cardíaco", es decir, el cliente verifica regularmente (cada segundo en nuestro caso) que el servidor todavía está allí, haciendo un pseudo-ping. Si no desea hacerlo, puede esperar a que el sistema operativo realmente detecte que la conexión se redujo, pero no espere que sea confiable ...

+1

@Gaetano, me sorprende que tienes el mecanismo de mantenimiento de conexión para trabajar en absoluto, francamente, incluso para el servidor. Como señala Mikarnage, un sistema de latido es el único mecanismo realmente confiable para todas las plataformas y las implementaciones de IP stack (AFAIK de todos modos). – AlastairG

3

Por lo tanto, después de una nueva investigación, incluso si "TCP Keepalive" no está destinado para este uso, he descubierto que las sondas de mantenimiento vivo se comienzan a enviar en una "conexión inactiva". La pregunta ahora es: "¿cuándo se considera una conexión en estado inactivo?". Una conexión se considera inactiva cuando no hay datos "transmitidos", por lo que si uno de los dos pares está bloqueado en un envío (...) en realidad se están transmitiendo algunos datos y la conexión no se considera inactiva. Supongo que la única opción que tengo ahora es hacer un ping/pong usando send/recv con timeout, declarando una conexión "perdida" cuando esos temporizadores caducan.

+0

No estoy del todo seguro, pero creo que esta línea explica (en entornos Linux) por qué keepalive no funciona cuando se usa send(): [tcp_timer.c] (https://github.com/torvalds/linux/blob/4d8a991d460d4fa4829beaffdcba45a217ca0fa7 /net/ipv4/tcp_timer.c#L663) – Gooseman

0

Gaetano, IMO, TCP keep-alives se pueden utilizar para detectar conexiones muertas. En su ejemplo, es posible que el cliente cuelgue en el envío esperando a que los reintentos del TCP se agoten. Dependiendo del algoritmo de retroceso y la máquina de estado de pila TCP, esto puede durar varios minutos sin sondas de mantenimiento de vida, y por lo tanto no hay forma de agotar el control.

Supongo que el servidor es principalmente de lectura bloqueada, en cuyo caso, sus keep-alive se enviarán cada keepidle/slowhz segundos (slowhz es a menudo 2 en lugar de 1), y detectará la pérdida de conexión de manera justa con rapidez.

Si captura un trazado de paquete con tcpdump, verá exactamente lo que está sucediendo en el cable.

Cuestiones relacionadas