setsockopt c
El socket TCP de Windows tiene habilitado SO_KEEPALIVE de manera predeterminada? (1)
Primero ejecute su prueba en una instalación limpia del sistema operativo en una máquina virtual. Sospecho que algo más que haya instalado ha alterado el entorno de mantener vivo, tal vez.
En segundo lugar, dudo que mantener vivo sea habilitado sea la causa de tu problema. Si keep alive no está habilitado, entonces nunca recibirás una notificación de cierre de conexión de esa lectura pendiente. Se supone que TCP funciona de esa manera, permite que los enrutadores intermedios se apaguen y regresen y que usted ni lo sepa ni le importe. La única vez que se le informará de la falla es si intenta enviar y la conexión se interrumpe (o, en este caso, si trata de enviar y el servidor ha rebotado). El hecho de que mantenerse vivo se habilitó significa que en ese lapso de 1 hora y 59 minutos la pila de TCP transmitió el mantenimiento y notó que la conexión estaba inactiva. Si mantener vivo no estaba habilitado, entonces habría tenido que esperar hasta que USTED haya transmitido algo.
Si sus clientes necesitan saber si la conexión falla, entonces es mejor ignorar mantener vivo por completo (como puede ver, afecta a toda la máquina incluso cuando usted no es la persona que la habilitó y para mí la hace una solución pobre) ) Si puede, agregue un ping y / o tiempo de espera de nivel de aplicación a su protocolo. Entonces, quizás, cada comando espera una respuesta dentro de los 30 segundos y usted envía un mensaje del servidor cada minuto ... A continuación, descubrirá la conexión muerta tan rápido como desee y podrá desconectarse y reconectarse en ese punto.
Lo he usado bastante bien con mi sistema de servidor ; de hecho, tengo un filtro de conexión estándar de "tiempo de espera de lectura asíncrona" y un filtro de "restablecimiento de la conexión" que hacen que sea trivial garantizar que las conexiones estén siempre activas. Todo el tiempo de espera de lectura es abortar la conexión existente y el código de restablecimiento de conexión se inicia para volver a crear la conexión como lo haría si la conexión se hubiera cerrado por cualquier otro motivo.
He encontrado un error extraño con los sockets TCP. Parece que SO_KEEPALIVE
está habilitado en todos los sockets de forma predeterminada.
Escribí un breve caso de prueba para crear un socket y conectarme a un servidor. Inmediatamente después de la conexión, SO_KEEPALIVE
con getsockopt
. El valor es distinto de cero, que de acuerdo con MSDN, significa que keep alive está habilitado. Tal vez estoy malinterpretando esto.
Recientemente tuve un error extraño en el que un servidor se desconectó dos veces seguidas. Algunos clientes se encontraban en un estado en el que habían enviado información de inicio de sesión y estaban esperando una respuesta. A pesar de que había un WSARecv
superpuesto publicado en el socket conectado al servidor, no se publicó ninguna finalización para notificar al cliente que el servidor se bloqueó, por lo que supongo que el socket no se cerró por completo.
Aproximadamente 2 horas después (en realidad, aproximadamente 1 hora, 59 minutos y 19 segundos), se publicó un paquete de finalización para la lectura, notificando al cliente que la conexión ya no está abierta. Aquí es donde comencé a sospechar SO_KEEPALIVE
.
Estoy tratando de entender por qué sucedió esto. Causó un pequeño problema porque se supone que los clientes que pierden su conexión por cualquier motivo se vuelven a conectar automáticamente al servidor; en este caso, debido a que no se notificó desconexión, el cliente no se volvió a conectar hasta 2 horas después.
Una solución obvia es poner un tiempo de espera, pero me gustaría saber cómo podría ocurrir esta situación.
SO_KEEPALIVE
no está configurado en el socket por mi servidor o cliente de aplicaciones.
// Error checking is removed for this snippet, but all winsock calls succeed.
int main() {
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 2);
err = WSAStartup(wVersionRequested, &wsaData);
SOCKET foo = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, 0, 0, 0);
DWORD optval;
int optlen = sizeof(optval);
int test = 0;
test = getsockopt(foo, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, &optlen);
std::cout << "Returned " << optval << std::endl;
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
clientService.sin_port = htons(446);
connect(foo, (SOCKADDR*) &clientService, sizeof(clientService));
test = getsockopt(foo, SOL_SOCKET, SO_KEEPALIVE, (char*)&optval, &optlen);
std::cout << "Returned " << optval << std::endl;
std::cin.get();
return 0;
}
// Example output:
// Returned 2883584
// Returned 2883584