Un socket abierto no es un equipo conectado
Un operador manda inmovilizar un vehículo. El servidor busca la conexión del equipo, escribe el comando en el socket y la escritura sale bien. Eso no demuestra que el equipo lo recibió, y mucho menos que lo ejecutó.
En rastreo vehicular, la conexión TCP es solamente un medio de comunicación. No es una garantía de disponibilidad. De eso trata esta nota.
Las cifras de los ejemplos son ilustrativas: salen de un simulador con una flota supuesta, no de mediciones de nuestros gateways ni de ningún operador.
Conexiones que mueren sin avisar
Hemos trabajado con gateways de recepción para equipos GPS, telemetría y cámaras vehiculares. Con JT808 y otros protocolos de rastreo, el equipo abre una conexión TCP persistente y la mantiene para mandar posiciones, alarmas y otros mensajes. Cómo se comporta depende del protocolo y del fabricante.
Con equipos móviles, esa conexión puede dejar de servir sin que el servidor reciba un cierre: el vehículo pierde la cobertura, cambia de red, o el NAT del operador olvida la conexión por inactividad. El servidor conserva un socket que parece abierto, aunque el equipo ya no tenga conectividad.
Tres maneras de enterarse
Hay tres mecanismos para saber que una conexión murió, y no son equivalentes:
- Actividad de la aplicación: cuánto tiempo ha pasado desde el último mensaje válido del equipo.
- El latido del protocolo: si el equipo sigue mandando sus mensajes periódicos de presencia.
- El keepalive de TCP: el mecanismo del sistema operativo para detectar conexiones que dejaron de responder.
En el simulador, una flota de 2,000 equipos manda una posición cada 30 segundos cuando se mueve y un latido cada cinco minutos cuando está parada, y cada equipo pierde la cobertura unas cuatro veces al día. En un día, 7,762 conexiones mueren sin aviso. Esto tarda el servidor en enterarse, en la mediana:
- Con tres latidos faltantes, 12.8 minutos.
- Con el keepalive de TCP con los valores de Linux (dos horas de inactividad y nueve sondas), 2.2 horas.
- Si además la sesión nueva del mismo equipo cierra la vieja, 10.3 minutos.
El keepalive del sistema, con sus valores de fábrica, no sirve para esto, y además viene apagado por omisión en cada socket. Y ninguno es instantáneo: durante esos minutos, el socket parece abierto.
No tengo documentado un intervalo de latido único para toda nuestra flota: depende del protocolo, del fabricante y de la configuración. Tampoco le atribuiría a un operador un tiempo de expiración del NAT sin haberlo medido. En el simulador son supuestos, y se mueven.
Lo que cuesta un latido
Un latido pequeño no cuesta sólo sus bytes. Un latido de 20 bytes, con las cabeceras de TCP/IP, su respuesta y el ACK, son unos 160. En el simulador, la flota gasta 1.22 MB al mes por equipo sólo en mantenerse conectada; contando nada más el latido y su respuesta parecerían 0.30. En una flota grande, esa diferencia pesa.
Alargar el latido para ahorrar tiene un límite: el NAT. Si el latido es de 15 minutos y el NAT olvida a los 10, cada latido de un equipo parado se topa con una conexión que ya no existe y obliga a reconectar. En el simulador, el gasto baja una cuarta parte, de 1.22 a 0.91 MB, y las conexiones que mueren sin aviso pasan de 7,762 a más de 170,000.
Enviado no es recibido
El caso del comando es el más delicado. Por eso distinguiría cuatro estados:
- Comando generado.
- Comando enviado a la conexión.
- Comando confirmado por el equipo.
- Acción ejecutada y verificada.
Para pasar del segundo al tercero hace falta una confirmación del protocolo; del tercero al cuarto, cuando corresponda, una verificación independiente del estado final.
En el día del simulador se mandan 988 comandos. De ellos, 24 se escriben en un socket que ya estaba muerto: el servidor los “envió” y nunca llegaron. Si “enviado” contara como hecho, serían 24 éxitos falsos. Con un ciclo de vida que espera la confirmación, ocho se reenvían por la sesión nueva y 16 vencen sin confirmarse, a la vista de quien los mandó. Y si el latido pasa del NAT, los comandos escritos en sockets muertos suben a 322.
Dos sesiones del mismo equipo
Un equipo pierde la comunicación, reconecta y abre una sesión nueva antes de que el servidor detecte que la anterior ya no sirve. Si el sistema administra las conexiones sólo por socket, puede quedarse con dos sesiones del mismo equipo.
Mi criterio es un registro de sesiones por equipo, con un identificador de sesión o generación. Cuando llega una sesión nueva, autenticada y válida, sustituye a la anterior bajo una política explícita, y la vieja se invalida y se cierra de forma controlada. También hay que impedir que el cierre tardío de la sesión vieja borre por accidente la referencia a la nueva. No lo presento como una falla que hayamos sufrido, sino como una condición que el diseño tiene que resolver.
El simulador compara los dos registros. En su día, 4,579 veces un equipo reconecta con la sesión vieja todavía abierta. Con un registro por socket, que registra al equipo con cada mensaje y borra la entrada al cerrarse cualquiera de sus sesiones, 4,566 de esos cierres tardíos borran la sesión nueva. El equipo sigue conectado y el sistema lo da por desconectado hasta su siguiente mensaje: 335 horas-equipo en el día. Si el registro sólo se actualizara al autenticarse, el equipo quedaría “desconectado” hasta volver a reconectar: 20,801 horas-equipo, y 429 comandos vencerían sin encontrar sesión. Con registro por generación, ninguna.
Varias instancias
Con varios gateways, cada equipo puede estar conectado a una instancia distinta. No basta con conocer su IMEI: hay que saber qué instancia tiene su conexión. Diseñaría un registro de sesiones distribuido con el identificador del equipo, la instancia, el identificador y la generación de la sesión, la última actividad válida, y su estado y vigencia.
El comando se dirige a esa instancia, y la instancia verifica que todavía tiene la sesión antes de transmitirlo. No lo mandaría a todos los gateways. Y hay que contemplar que el equipo cambie de instancia con el comando en tránsito. El registro localiza la conexión, pero no sustituye la confirmación del protocolo ni garantiza que el comando se ejecutó.
El reinicio que se vuelve segunda falla
Cuando se reinicia un gateway, las sesiones de ese proceso se cortan. Lo que sigue depende de los equipos: unos reconectan a intervalos fijos, otros con otras estrategias, y no siempre se controla desde la plataforma. No tengo documentado un incidente de reconexión masiva ni una cifra de equipos que hayan reconectado a la vez. Lo que sí considero indispensable es que la recuperación no se convierta en una segunda falla.
Y hay que distinguir tres cosas: una conexión TCP aceptada, un equipo autenticado y una sesión lista para recibir comandos. En el simulador, el gateway queda fuera 15 segundos; acepta 1,000 conexiones por segundo con una cola de 511 (la que usa Node.js si no se le dice otra), autentica 100 equipos por segundo e inicializa 200 sesiones. Con 2,000 equipos que reintentan juntos cada 30 segundos, llegan 2,000 intentos en un segundo. La cola se llena: Linux descarta 3,323 SYN, que los equipos reenvían; 900 intentos se abandonan por esperar de más, y todas las sesiones quedan listas para comandos a los 79 segundos. Con una espera al azar de hasta un minuto, el pico es de 74 intentos por segundo, no se descarta nada y todas quedan listas a los 60 segundos, un tiempo que no puede bajar de esa espera: el último equipo no intenta antes. Con 5,000 equipos la diferencia crece: 195 segundos contra 64, y con intervalo fijo se descartan 27,363 SYN.
Lo que importa no es cuántos intentos llegan por segundo, sino cuánto tarda el sistema en recuperar todas las sesiones. En un diseño nuevo usaría reinicios escalonados cuando la arquitectura lo permita, distribución de carga y esperas al azar en la reconexión cuando el firmware lo soporte.
Los límites que no se ven
No tengo documentado un incidente por falta de descriptores, memoria o puertos en nuestros gateways de rastreo. Pero son parte de lo que hay que dimensionar: los descriptores disponibles, la memoria por conexión, el uso del event loop, las colas de aceptación de TCP y los estados de los sockets. No asumiría que un servidor soporta miles de conexiones sólo porque tiene memoria y CPU de sobra.
Lo que haría hoy
En nuestras arquitecturas actuales usamos principalmente Node.js y NestJS para la recepción TCP, con servicios especializados por protocolo, y RabbitMQ para desacoplar la recepción del procesamiento. Si diseñara hoy un gateway desde cero para miles de equipos:
Separaría cuatro responsabilidades. Transporte: sockets, conexiones, desconexiones, timeouts y bytes. Protocolo: identificar equipos, interpretar tramas, validar mensajes y gestionar confirmaciones. Sesiones: la identidad del equipo, su conexión vigente y la instancia que la tiene. Mensajería y procesamiento: entregar la telemetría al resto de la plataforma sin bloquear la recepción.
Le daría a los comandos un ciclo de vida propio, con identificadores únicos, estados de ejecución, vencimientos y confirmaciones.
Mediría desde el principio cuántas conexiones hay, cuántas son de verdad activas, cuántas reconexiones ocurren por minuto, cuánto se tarda en detectar una sesión muerta y cuántos comandos quedan sin confirmar.
Haría pruebas de carga que no se limiten a mandar posiciones: miles de conexiones persistentes, desconexiones silenciosas, sesiones duplicadas y reinicios del gateway.
Para probarlo con otra flota, otro latido, otro NAT o otro gateway, está el simulador de conexiones: corre en el navegador, y todo lo que dice es modelo, no medición.
Un socket abierto no demuestra que el dispositivo esté conectado de manera efectiva. Un comando enviado no demuestra que haya llegado. Y una confirmación de recepción no necesariamente demuestra que la acción se haya ejecutado. Cuando administramos muchos dispositivos, la confiabilidad depende menos de cuántas conexiones podemos abrir y más de nuestra capacidad para conocer el estado real de cada sesión y recuperarnos correctamente cuando las comunicaciones fallan.
El verdadero desafío no es mantener miles de sockets abiertos, sino administrar miles de dispositivos cuya conectividad puede cambiar en cualquier momento.
Israel Negrete Lepe es ingeniero en electrónica. Veinticinco años haciendo que sistemas distintos funcionen juntos en condiciones reales: del sensor, el vehículo o la transacción al backend, los datos, la seguridad y la operación. Hoy, cada vez más, IA en el borde.