Escena conocida: las solicitudes cortas vuelan como golondrinas, pero en cuanto inicias una gran subida o descarga de un archivo pesado a través de un proxy móvil, la conexión se corta a la mitad. El archivo al 40 por ciento, luego silencio. Repetir la solicitud a veces ayuda, a veces no. Y lo más frustrante: los registros están en silencio, el servidor parece vivo, pero los datos no llegan. Si te ha pasado, estás en el lugar correcto.

Este artículo no trata sobre códigos de respuesta ni sobre la lógica de reintentos a nivel HTTP (sobre 429 y el backoff exponencial hay un material aparte, al que haremos referencia). Aquí analizamos el nivel de transporte: por qué se corta TCP, quién exactamente rompe la conexión, cómo funcionan tres timeouts diferentes en el camino del paquete, qué es MTU y la fragmentación en redes celulares, y por qué el cambio de estación base mata las sesiones largas. Y lo principal: construiremos un cliente que sobreviva a todo esto.

Introducción: la descarga se cae a la mitad, las solicitudes cortas pasan

¿Por qué el tema es tan relevante justo en 2026? Porque los proxies móviles se han convertido en una herramienta de trabajo para scraping, automatización, pruebas y trabajo con marketplaces. La red celular es por naturaleza más inestable que la cableada. Fue diseñada para una persona con un teléfono que abre una página, la lee y la cierra. No fue diseñada para una máquina que mantiene una conexión TCP abierta durante diez minutos y descarga un gigabyte a través de ella.

De ahí la paradoja del título de esta sección. Las solicitudes cortas pasan porque logran completarse antes de que cualquier timeout se active o ocurra un handover (cambio entre estaciones base). Las operaciones largas viven más tiempo y, por lo tanto, aumentan la probabilidad de encontrarse con cada uno de los problemas: timeout de inactividad, corte por cambio de celda, pérdida de paquetes grandes debido a MTU incorrecto.

Qué aprenderás al leer hasta el final:

  • Cómo entender en cinco minutos quién exactamente cortó la conexión: tu cliente, el proxy, el operador o el servidor de destino.
  • En qué se diferencia TCP keepalive de HTTP keep-alive y por qué necesitas ambas configuraciones.
  • Cómo medir los tres timeouts en el camino y encontrar el más corto, que decide el destino de la conexión.
  • Por qué en las redes celulares el MTU es menor que los 1500 bytes estándar y cómo se ve la rotura de PMTUD (síntoma: congelamiento en respuestas grandes).
  • Código listo de cliente en Python y Node con reanudación por Range, división de subidas y timeouts adecuados.

Hablaremos en lenguaje técnico, pero explicaremos cada término de forma sencilla. Vamos.

Fundamentos: cómo funciona una conexión a través de un proxy móvil

Antes de analizar las fallas, acordemos la visión del mundo. Cuando haces una solicitud a través de un proxy móvil, el paquete recorre una larga cadena. Comprender esta cadena es la mitad del éxito en el diagnóstico.

El camino del paquete desde el cliente hasta el servidor

Imagina la ruta de un paquete. Pasa por etapas, y en cada una pueden retrasarlo o perderlo:

  1. Tu cliente: el programa que hace la solicitud. Tiene sus propios timeouts y configuraciones de socket.
  2. El servidor proxy: acepta tu conexión y abre la suya hacia el destino. A menudo son dos conexiones TCP diferentes unidas.
  3. El módem móvil y la interfaz de radio: el tramo estrecho y caprichoso. Aquí el paquete viaja por ondas de radio hasta la estación base.
  4. El núcleo de la red del operador: NAT, pasarelas, sistemas de priorización de tráfico.
  5. Internet público: canales troncales hasta el centro de datos del destino.
  6. El servidor de destino: el punto final, que también tiene sus límites sobre la duración de la conexión.

La idea clave: la conexión a través de un proxy no es un solo tubo, sino al menos dos. El cliente mantiene una conexión con el proxy, y el proxy mantiene una conexión con el destino. El corte puede ocurrir en cualquiera de estos segmentos, y los síntomas serán diferentes.

Qué es una sesión y por qué es frágil

Una conexión TCP es un canal virtual. Físicamente no hay ningún cable entre tú y el servidor: hay un acuerdo entre las dos partes de que intercambian bytes numerados. Mientras ambas partes recuerden los números y el estado, la conexión está viva. En cuanto una de las partes olvida el estado (el NAT se reinició, el timeout expiró, la celda cambió), la conexión está efectivamente muerta, aunque la otra parte puede no saberlo por mucho tiempo.

Esto crea las traicioneras conexiones semiabiertas (half-open): una parte considera el canal vivo y espera datos, mientras la otra ya lo olvidó todo. El cliente se queda colgado, no llega ningún error, el tiempo pasa. ¿Te suena?

Tres niveles donde ocurren problemas

  • Nivel de radio: pérdida de paquetes, retrasos, cambio de celda. La naturaleza de la red celular.
  • Nivel de red: timeouts NAT del operador, MTU, fragmentación.
  • Nivel de aplicación: timeouts del servidor HTTP del proxy y del destino, límites en el tamaño del cuerpo de la respuesta.

A continuación, repasaremos cada uno y aprenderemos a distinguirlos.

Inmersión profunda: quién puede cortar la conexión y cómo saber quién fue

Esta es la sección de diagnóstico más importante. Si no sabes quién es el culpable, disparas a ciegas. La buena noticia: cada culpable tiene su propia firma.

Cuatro sospechosos

1. Tu cliente. El culpable más frecuente y más subestimado. Las bibliotecas HTTP tienen timeouts predeterminados que quizás no notaste. Por ejemplo, un timeout en el tiempo total de la solicitud, un timeout de lectura del socket, un timeout de inactividad. Si tu cliente cortó la conexión solo, en los registros verás un error como read timeout o socket timeout con tu código en el stacktrace.

2. El servidor proxy. El proxy tiene sus propios límites: tiempo máximo de vida de la conexión, idle timeout, tamaño máximo de respuesta. Cuando el proxy corta la conexión, a menudo recibes un connection reset repentino o un cierre sin respuesta en medio de la transferencia del cuerpo. Mientras tanto, el ping al proxy funciona y las solicitudes cortas funcionan.

3. El operador de telefonía. El más invisible. Las tablas NAT del operador tienen un tiempo de vida para las entradas. Si durante un tiempo no hay tráfico a través de la conexión, el operador elimina la entrada del NAT y los paquetes posteriores ya no tienen a dónde ser entregados. Síntoma: la conexión se congela precisamente durante las pausas, no durante la transferencia activa. El operador también es culpable en el cambio de estación base.

4. El servidor de destino. Tiene sus propios ajustes de keep-alive y límites. Muchos servidores cierran la conexión después de N solicitudes o después de T segundos. Síntoma: el servidor envía el encabezado Connection: close o cierra correctamente la conexión (FIN), no la rompe (RST).

Diagnóstico por síntomas: tabla de firmas

Veamos las señales características para que puedas identificar al culpable en minutos.

  • Corte estrictamente durante las pausas, durante la transferencia activa todo va bien: casi con seguridad es el timeout NAT del operador o el idle timeout del proxy. Se cura con tráfico keepalive.
  • Corte siempre aproximadamente en el mismo volumen de datos (por ejemplo, alrededor de 8 o 10 megabytes): es el límite de tamaño de respuesta del proxy o del servidor. Se cura dividiendo con Range.
  • Corte siempre aproximadamente después del mismo tiempo (por ejemplo, exactamente 60 o 300 segundos): es un límite duro de duración de la conexión. Busca el menor de los timeouts.
  • Congelamiento justo en respuestas grandes, las pequeñas pasan: es la clásica rotura de PMTUD y el problema de MTU. Hay una sección completa sobre esto más abajo.
  • Cortes aleatorios sin relación con volumen o tiempo, más frecuentes en movimiento: cambio de estación base, handover, degradación de la señal de radio.
  • RST inmediato al intentar transferir datos: la conexión ya está muerta en un lado (semiabierta) o es un reinicio activo del proxy.

Herramientas de diagnóstico primario

Para distinguir FIN (cierre cortés) de RST (reinicio brusco) y entender en qué segmento se corta, utiliza:

  • Registro a nivel de socket: anota la hora exacta del corte, la cantidad de bytes transferidos y el tipo de excepción.
  • Análisis de tráfico: una herramienta de captura de paquetes mostrará quién envió RST o FIN. Si el RST viene de la dirección del proxy, el culpable es el proxy. Si la conexión simplemente se queda en silencio sin paquetes, el culpable es un nodo intermedio (el operador).
  • Mediciones de control: repite la misma operación con una conexión cableada directa. Si por cable todo es estable y con el proxy móvil se corta, el problema está en el segmento móvil.

Observación del autor: en el 70 por ciento de los casos que hemos analizado, el culpable resultó ser el timeout NAT del operador o un idle timeout predeterminado, no el proxy ni el servidor. La gente culpa al proxy, y la solución está en un par de líneas de configuración del socket.

Keep-alive e idle timeout: tres timeouts en el camino, gana el menor

Aquí está el principio fundamental que deberías tatuarte en el subconsciente: en el camino del paquete viven varios timeouts de inactividad independientes, y el destino de la conexión lo decide el más corto de todos. Es como una cadena: se rompe por el eslabón más débil.

Dónde viven los timeouts

  1. Idle timeout de tu cliente. Cuánto tiempo está dispuesto tu programa a esperar datos sin recibir nada. Por defecto, en diferentes bibliotecas va desde 30 segundos hasta infinito.
  2. Idle timeout del proxy. Cuánto tiempo mantiene el proxy abierta una conexión inactiva. Los valores típicos van de 60 a 300 segundos.
  3. Timeout NAT del operador. Cuánto tiempo almacena el núcleo de la red la entrada de traducción de direcciones sin tráfico. Para TCP, a menudo es de 300-600 segundos, pero para UDP y en horas de alta carga puede ser de 30-60 segundos.
  4. Timeout keep-alive del servidor de destino. Cuánto tiempo mantiene el servidor la conexión entre solicitudes. A menudo es de 5 a 75 segundos.

Imagina que el cliente está dispuesto a esperar 120 segundos, el proxy corta a los 90, y el operador limpia el NAT a los 60. ¿Quién gana? El operador. La conexión morirá en el segundo 60 de inactividad, y ni el cliente ni el proxy lo sabrán de inmediato.

Cómo medir el timeout más corto

El método es simple y confiable. Establecemos una conexión, hacemos una solicitud, luego callamos y esperamos, cronometrando el tiempo hasta el corte. Repetimos varias veces para descartar el azar.

  1. Abre una conexión a través del proxy a un servidor de prueba que pueda mantener keep-alive.
  2. Haz una solicitud corta y recibe la respuesta.
  3. No cierres la conexión. Inicia un temporizador.
  4. Periódicamente (una vez por segundo) verifica si la conexión está viva, intentando leer del socket en modo no bloqueante.
  5. Anota el momento en que llega RST, FIN o el socket deja de ser legible.

Realiza la medición tres veces. Si el corte ocurre consistentemente alrededor de 60 segundos, ese es tu límite de inactividad. Entonces, el tráfico keepalive debe enviarse con más frecuencia que cada 60 segundos, con un buen margen, por ejemplo, cada 20-25 segundos.

Estrategia para vencer el idle timeout

Dado que el timeout más corto lo decide todo, nuestra tarea es no permitir que la conexión quede inactiva más allá de ese límite. Dos enfoques:

  • Llenar la conexión con tráfico útil: durante la transferencia activa de datos, los timeouts idle no se activan porque no hay inactividad. Por eso, una descarga continua rara vez sufre por idle timeout, pero sí sufre por límites de tamaño y tiempo de vida.
  • Enviar sondas keepalive en las pausas: cuando no hay datos útiles (por ejemplo, esperando la generación de un informe en el servidor), hay que mantener la conexión con tráfico artificial. Aquí entran en juego los dos mecanismos keep-alive, que analizaremos por separado.

TCP keepalive vs HTTP keep-alive: mecanismos diferentes, se necesitan ambos

Una gran confusión en la industria nace de los nombres similares. TCP keepalive y HTTP keep-alive son cosas completamente diferentes, que funcionan en niveles distintos. Y para un cliente móvil robusto, se necesitan ambas.

HTTP keep-alive: reutilización de la conexión

HTTP keep-alive (también conocido como conexión persistente) se trata de no abrir una nueva conexión TCP para cada solicitud. En su lugar, una sola conexión atiende varias solicitudes seguidas. Es el nivel de aplicación.

¿Por qué es importante para las redes móviles? Establecer una nueva conexión TCP a través de una red celular es costoso. El triple apretón de manos más, si se usa cifrado, un handshake TLS. Por el canal de radio con grandes latencias, esto puede tomar cientos de milisegundos. Al reutilizar la conexión, ahorras ese tiempo en cada solicitud posterior.

¡Pero! El HTTP keep-alive no ayuda en absoluto contra el timeout NAT del operador en las pausas. Solo permite mantener la conexión abierta para la siguiente solicitud, pero por sí mismo no genera tráfico en los intervalos.

TCP keepalive: pulso a nivel de transporte

TCP keepalive es un mecanismo del propio protocolo TCP. El sistema operativo envía periódicamente un paquete de servicio vacío (keepalive probe) para verificar si la otra parte está viva y, de paso, refrescar las entradas en las tablas NAT intermedias. Es el nivel del núcleo del SO.

Precisamente TCP keepalive es tu principal arma contra los timeouts NAT del operador y los idle timeouts de los nodos intermedios. Cada sonda es tráfico que restablece el contador de inactividad en todo el camino.

TCP keepalive tiene tres parámetros clave:

  • keepalive idle (o keepalive time): después de cuántos segundos de inactividad comenzar a enviar sondas. Por defecto en la mayoría de los sistemas operativos son 7200 segundos, es decir, dos horas. Una catástrofe para las redes móviles.
  • keepalive interval: el intervalo entre sondas si no hay respuesta.
  • keepalive count (probes): cuántas sondas sin respuesta consecutivas se consideran señal de conexión muerta.

Valores recomendados para redes móviles

Los dos horas predeterminados no sirven en absoluto. El operador limpiará el NAT mucho antes de la primera sonda. Aquí hay valores de trabajo, probados en la práctica:

  • keepalive idle: 15-25 segundos. Empezamos a enviar sondas después de una pausa corta, para adelantarnos al timeout NAT más agresivo.
  • keepalive interval: 10-15 segundos. Si la sonda no llegó, repetimos a un intervalo corto.
  • keepalive count: 3-4. Después de tres o cuatro sondas sin respuesta, declaramos la conexión muerta y la restablecemos, sin esperar un congelamiento infinito.

Esta combinación da un doble beneficio: la conexión no se corta por el operador en las pausas, y si muere de todos modos (por ejemplo, por un handover), el cliente lo sabrá en 45-70 segundos, no en horas. La detección rápida de una conexión muerta es la mitad de la robustez.

Por qué se necesitan ambas configuraciones

Una analogía. HTTP keep-alive es como dejar la puerta de la sala de reuniones reservada todo el día, para no tener que reservarla de nuevo cada vez. Y TCP keepalive es entrar periódicamente a esa sala para que el conserje no piense que está vacía y la cierre con llave. Si la reservas pero no entras, la cierran. Si entras pero reservas de nuevo cada vez, pierdes tiempo. Se necesitan ambas.

MTU y fragmentación: por qué en las redes celulares los paquetes son más pequeños

Pasemos a la causa más subestimada de congelamientos. Si tienes respuestas pequeñas que pasan y las grandes se congelan para siempre, con un 90 por ciento de probabilidad se trata de MTU y PMTUD roto.

Qué es MTU en palabras simples

MTU (Maximum Transmission Unit) es el tamaño máximo de un paquete que se puede enviar a la red sin dividirlo. En el Ethernet clásico son 1500 bytes. Imagina que es el ancho de una puerta: los muebles más anchos que la puerta habrá que desarmarlos en partes para poder pasarlos.

El problema es que en las redes celulares el MTU efectivo suele ser inferior a 1500. La razón es la encapsulación. El tráfico móvil se envuelve en capas de protocolo adicionales dentro de la red del operador. Cada capa agrega sus bytes de cabecera, dejando menos espacio para los datos útiles. El MTU real de trabajo en redes móviles suele estar en el rango de 1400-1480 bytes, a veces incluso menos.

Qué es PMTUD y por qué se rompe

PMTUD (Path MTU Discovery) es un mecanismo para determinar automáticamente el tamaño máximo de paquete en toda la ruta. Funciona así: el cliente envía un paquete grande con el flag Don't Fragment (no fragmentar). Si en el camino encuentra un nodo con un MTU menor, ese nodo descarta el paquete y envía de vuelta un mensaje ICMP especial llamado fragmentation needed con el tamaño permitido. El cliente recibe la sugerencia y reduce el tamaño de los paquetes.

Un esquema bonito. Pero se sostiene sobre una condición frágil: los mensajes ICMP deben llegar de vuelta al cliente. Y en la realidad, muchas redes, firewalls y configuraciones de seguridad bloquean ICMP por completo. Entonces ocurre la catástrofe llamada PMTUD black hole (agujero negro).

Cómo se ve la rotura de PMTUD: anatomía de un congelamiento

El escenario es clásico y muy característico:

  1. El cliente establece la conexión. El apretón de manos pasa con paquetes pequeños: todo perfecto.
  2. El cliente envía una solicitud corta: el paquete pequeño pasa, la respuesta llega. Las respuestas pequeñas funcionan.
  3. El servidor comienza a enviar una respuesta grande con paquetes de tamaño completo de 1500 bytes con el flag Don't Fragment.
  4. En algún lugar del camino, un nodo con MTU 1400 descarta estos paquetes porque son más grandes y no se pueden fragmentar.
  5. El nodo envía de vuelta ICMP fragmentation needed, pero ese ICMP es bloqueado por el firewall y no llega.
  6. El servidor no recibe la sugerencia, continúa enviando los mismos paquetes grandes, que vuelven a ser descartados. Un ciclo infinito.
  7. Resultado: los paquetes pequeños (cabeceras, ACK) pasan, pero los datos de la respuesta no. La conexión se congela en seco en medio de una respuesta grande.

Por eso las solicitudes cortas pasan y las respuestas grandes se congelan. No es mística, es un agujero negro de PMTUD. Y es quizás la fuente más común de canas en los ingenieros que trabajan con tráfico móvil.

Cómo tratar el problema de MTU

Hay varios niveles de solución, desde el nivel de aplicación hasta el de red:

  • MSS clamping. El método más confiable a nivel de red. MSS (Maximum Segment Size) es el tamaño máximo del segmento TCP que las partes acuerdan al principio, durante el apretón de manos. Si se limita explícitamente MSS para que el paquete final quepa en el MTU real (por ejemplo, MSS 1360-1400), el servidor desde el principio enviará paquetes del tamaño adecuado y no se necesitará ICMP. Esto cura el agujero negro de raíz.
  • Reducir el MTU de la interfaz. Si controlas el módem o la máquina a través de la cual viaja el tráfico, puedes establecer el MTU de la interfaz en 1400 o menos. Entonces todos los paquetes salientes serán, por diseño, no más grandes que el tamaño seguro.
  • Permitir el paso de ICMP. Si tienes control sobre los firewalls en el camino, permite los mensajes ICMP del tipo fragmentation needed. Entonces el PMTUD estándar funcionará como se espera.
  • Nivel de aplicación. Si descargas un archivo grande, divídelo en partes mediante solicitudes Range. Cada parte es una transferencia corta separada, con menos probabilidad de toparse con problemas de flujos grandes. Más detalles en la sección práctica.

Insight: incluso si no controlas la red, casi siempre puedes influir en el MSS a través de la configuración de la conexión o mediante los parámetros del servicio proxy. Un buen proveedor de proxies móviles ya configura un MSS clamping correcto en su lado, evitando agujeros negros. Este es uno de los criterios de calidad del servicio.

Cambio de estación base y traspaso entre redes: por qué TCP no lo sobrevive

Ahora sobre la incompatibilidad más fundamental. La red celular fue creada para la movilidad, y TCP no. Y es un conflicto a nivel de arquitectura.

Por qué TCP está atado a la dirección

Una conexión TCP se define unívocamente por una cuaterna: dirección de origen, puerto de origen, dirección de destino, puerto de destino. Es como la dirección postal de la conexión. Si un solo elemento cambia, ya es otra conexión, y la anterior deja de ser válida.

¿Y qué pasa en una red móvil? En algunos escenarios de cambio de estación base o al pasar entre tecnologías (por ejemplo, entre diferentes generaciones de red o entre redes), la dirección IP externa del nodo móvil puede cambiar. Y aquí TCP se vuelve impotente: no sabe continuar una conexión con una dirección nueva. La conexión anterior simplemente está muerta. Todos los datos que estaban en tránsito se pierden.

Handover: no siempre fatal, pero siempre arriesgado

Para ser justos, las redes modernas intentan hacer el handover sin interrupciones: mantener la misma IP al cambiar de celda. A menudo lo logran y no notas el cambio. Pero a veces ocurre un cambio de IP, una interrupción corta del canal de radio de uno o dos segundos, o un pico de pérdida de paquetes. Para una solicitud corta es imperceptible. Para una sesión de diez minutos es una lotería en la que juegas cada vez.

Síntomas de corte por handover

  • Los cortes son aleatorios, no relacionados con el volumen de datos ni con el tiempo.
  • La frecuencia de cortes aumenta si el dispositivo se mueve físicamente (típico de los proxies móviles con SIM reales).
  • Después del corte, la nueva conexión se establece normalmente: la red está viva, solo murió la conexión anterior específica.

Qué hacer al respecto: aceptarlo y construir robustez

No se puede vencer el cambio de IP a nivel de TCP. Pero se puede construir un cliente para el cual el corte de conexión sea una situación normal, no de emergencia. La filosofía es simple: la conexión a través de una red móvil es efímera por definición, y el cliente debe poder restablecerla sin problemas y continuar desde donde se detuvo.

Las técnicas concretas que implementaremos en el código:

  • Operaciones idempotentes. Diseña tus solicitudes para que puedan repetirse de forma segura. La descarga por Range es idempotente: repetir la solicitud del mismo rango de bytes da el mismo resultado.
  • Reanudar, no volver a empezar. Ante un corte, no empieces el archivo de nuevo, continúa desde el último byte recibido. Ahorra tráfico y tiempo.
  • Detección rápida de la muerte de la conexión. Con TCP keepalive agresivo, del que hablamos, te enteras de una conexión muerta en segundos, no en minutos.
  • Transacciones cortas. Cuanto más corta es una transferencia individual, menor es la probabilidad de que un handover la atrape. Dividir una subida grande en partes es una consecuencia directa de este principio.

Práctica en Python: cliente robusto con reanudación y timeouts

Basta de teoría, vamos a construir. Empecemos con Python. Armemos un cliente que: configura TCP keepalive, define timeouts razonables, descarga archivos por partes mediante Range y puede reanudar después de un corte.

Paso 1: configuración de TCP keepalive en el socket

Las bibliotecas HTTP estándar no establecen un keepalive agresivo de fábrica. Hay que llegar al socket. En el ecosistema requests, esto se hace a través de un adaptador de transporte personalizado que configura las opciones del socket: activa keepalive, define idle 20 segundos, interval 10 segundos, count 3. A nivel de descripción, la lógica es: registramos un adaptador que al crear la conexión establece los parámetros necesarios en el nivel bajo.

La parte conceptual de la configuración en pseudocódigo, en palabras: activar la opción SO_KEEPALIVE, luego establecer TCP_KEEPIDLE en 20, TCP_KEEPINTVL en 10, TCP_KEEPCNT en 3. Los nombres de las opciones varían ligeramente entre sistemas operativos, por lo que en producción se seleccionan verificando la plataforma.

Paso 2: timeouts razonables

Regla clave: siempre define los timeouts explícitamente y separa el timeout de conexión del timeout de lectura. El valor predeterminado sin timeout es una trampa donde el cliente se cuelga para siempre en una conexión semiabierta.

  • Connect timeout: 10 segundos. Establecer una conexión a través de una red móvil es más lento, pero 10 segundos con margen son suficientes.
  • Read timeout: 30 segundos. Es el timeout de inactividad entre porciones de datos, no de toda la transferencia. Si en 30 segundos no llega ni un byte, la conexión se considera problemática.
  • Presupuesto total de la operación: contrólalo por separado a nivel de la lógica de reanudación, no con un solo timeout gigante para toda la solicitud.

Paso 3: descarga por Range con reanudación

La idea del algoritmo. Abrimos el archivo de destino para escritura. Averiguamos el tamaño ya descargado (si el archivo existe parcialmente). Solicitamos el rango restante mediante la cabecera Range: bytes desde la posición actual hasta el final. Escribimos el flujo en el archivo a medida que llega. Si atrapamos un corte (excepción de lectura, desconexión), no entramos en pánico: volvemos a mirar cuántos bytes hay ya en el disco y repetimos la solicitud con la nueva posición inicial. Así continuamos hasta que el archivo esté completo.

Paso a paso, la lógica en palabras:

  1. Determinar el tamaño total del archivo con una solicitud de cabeceras (HEAD o GET con Range: bytes=0-0, para leer Content-Range).
  2. Comprobar el tamaño local de la parte ya descargada.
  3. Si el tamaño local es igual al total, el archivo está listo, salimos.
  4. Si no, formar una solicitud con Range comenzando desde el tamaño local.
  5. Leer la respuesta en porciones de 64-256 kilobytes, añadiendo al archivo.
  6. Al completar con éxito, verificar la integridad (tamaño, y si es posible, suma de verificación).
  7. Ante un corte, incrementar el contador de intentos, aplicar una pausa corta y volver al punto 2.
  8. Limitar el número máximo de intentos a nivel de transporte (por ejemplo, 5-8), para no dar vueltas para siempre ante un problema sistémico.

Un detalle importante: asegúrate de que el servidor soporte Range. La señal es la cabecera Accept-Ranges: bytes en la respuesta y el código 206 Partial Content en la solicitud Range. Si el servidor devuelve 200 e ignora Range, la reanudación no es posible y habrá que descargar completo, pero entonces son especialmente importantes el keepalive y un MSS correcto.

Paso 4: dividir una subida grande

Cuando no descargas, sino que subes un gran volumen (por ejemplo, envías datos o recibes un informe grande), aplica el mismo principio de división. Divide la tarea en páginas o fragmentos de tamaño fijo. Cada fragmento es una transacción corta separada con su propio manejo de errores. Lleva un registro del progreso: qué fragmentos ya están confirmados. Ante un corte, repite solo los no confirmados. Esto convierte una operación frágil de diez minutos en una serie de transacciones cortas robustas.

En cuanto a las estrategias de reintento por códigos de respuesta del servidor, aquí no las tratamos, porque es un tema de un material aparte sobre 429 y backoff exponencial, al que deberías pasar después de este artículo. Nuestro enfoque es el transporte: corte, timeout, tamaño.

Práctica en Node.js: agente HTTP robusto y streaming

Ahora el mismo conjunto de principios en el ecosistema Node.js. Aquí el papel central lo juega el objeto agente, que gestiona el pool de conexiones.

Paso 1: configuración del agente con keep-alive

En Node, se crea un agente HTTP o HTTPS con keepAlive activado. El parámetro keepAlive true hace que el agente reutilice las conexiones (es el nivel HTTP). Además, se define keepAliveMsecs: el intervalo con el que se envían las sondas TCP keepalive a nivel de socket. Para redes móviles, establece keepAliveMsecs en torno a 15000-20000 milisegundos, para adelantarte al timeout NAT del operador. También limita maxSockets y maxFreeSockets para no crear conexiones innecesarias.

Paso 2: timeouts en diferentes niveles

En Node, los timeouts se definen en varios lugares y es importante no olvidar ninguno:

  • Timeout de establecimiento de conexión: mediante la opción en la solicitud o a través del manejador del evento de conexión del socket.
  • Timeout de inactividad del socket: el método que establece el timeout del socket. Cuando se activa, hay que destruir explícitamente el socket y tratarlo como un corte. Node no cierra el socket automáticamente en el timeout, solo emite el evento; esto se olvida a menudo y la conexión sigue colgada.
  • Manejo de eventos error y close en la solicitud y en el socket: cada uno debe conducir a una lógica controlada de reintento.

Los valores recomendados son similares a Python: connect alrededor de 10 segundos, idle del socket alrededor de 30 segundos, sondas keepalive cada 15-20 segundos.

Paso 3: streaming de descarga con reanudación

En Node es natural trabajar con flujos (streams). La lógica de reanudación es la misma que en Python: verificamos el tamaño del archivo local, abrimos un flujo de escritura en modo añadir, formamos una solicitud con la cabecera Range comenzando desde el tamaño actual, suscribimos manejadores a los eventos data, end y error. En el evento data escribimos el fragmento al archivo. En el evento end verificamos si se recibió el archivo completo. En el evento error o cierre prematuro, cuando se recibió menos de lo esperado, iniciamos el reintento desde la nueva posición.

El punto clave de robustez en Node es el manejo correcto de la finalización prematura del flujo. El evento end puede llegar aunque no se haya recibido todo, si la conexión se interrumpió. Por lo tanto, siempre compara el volumen realmente recibido con el esperado de la cabecera Content-Length o Content-Range. No te confíes solo del hecho de que end haya ocurrido.

Paso 4: envoltura de reintentos a nivel de transporte

Envuelve toda la operación de descarga en un bucle con un número limitado de intentos. Entre intentos, una pausa corta (para cortes de transporte basta con 1-3 segundos, porque la causa no es una sobrecarga del servidor, sino un evento de red). Cuenta los intentos. Al agotar el límite, propaga el error hacia arriba con información de diagnóstico: cuántos bytes se recibieron, qué tipo de error fue, cuántos intentos se hicieron. Este diagnóstico es invaluable al analizar incidentes.

Errores típicos: lo que no debes hacer

Repasemos los antipatrones que encontramos regularmente en código ajeno. Evítalos y la mitad de los problemas desaparecerá.

Error 1: falta de timeouts explícitos

El más común y el más doloroso. Un cliente sin timeout en una conexión semiabierta se cuelga para siempre. El hilo queda bloqueado, el recurso no se libera y piensas que la operación sigue en curso. Siempre define timeouts explícitos. La ausencia de timeout no es paciencia infinita, es una bomba oculta.

Error 2: confiar en el TCP keepalive predeterminado

Los dos horas predeterminados hacen que TCP keepalive sea inútil para redes móviles. Muchos activan SO_KEEPALIVE y se quedan tranquilos, sin sospechar que la primera sonda solo saldrá a los 7200 segundos, cuando el operador ya limpió todo. Configura idle, interval y count explícitamente.

Error 3: volver a descargar el archivo completo en cada corte

Un corte al 95 por ciento y reiniciar desde cero no solo es tiempo perdido, sino también un gasto extra de tráfico móvil, que en los proxies normalmente se cobra. La reanudación por Range es obligatoria para cualquier descarga grande.

Error 4: ignorar MTU y MSS

La gente lucha durante meses contra los congelamientos en respuestas grandes, probando timeouts y proxies, cuando la causa es un agujero negro de PMTUD. Si las respuestas grandes se congelan y las pequeñas pasan, revisa primero el MTU y configura el MSS clamping. Te ahorrará semanas.

Error 5: confundir cortes de transporte con respuestas del servidor

Un corte de transporte (RST, timeout de lectura, socket muerto) y una respuesta del servidor con código de error son situaciones diferentes con estrategias de reacción diferentes. Para los cortes de transporte son apropiados los reintentos rápidos con pausa corta y reanudación. Para respuestas del servidor con códigos como 429 se necesita backoff exponencial, y ese es el tema de otro artículo. No mezcles estas dos capas en un solo manejador.

Error 6: reintentos demasiado agresivos

Un bucle infinito de reintentos sin límite ante un problema sistémico (por ejemplo, el proxy está completamente inaccesible) se convierte en una carga parasitaria. Siempre pon un tope al número de intentos y termina la operación de forma sensata con diagnóstico.

Error 7: no verificar la integridad del resultado

Que la descarga haya terminado no es igual a que los datos sean correctos. Un corte puede dejar un archivo truncado que formalmente parece completo. Siempre compara el tamaño, y para datos importantes, la suma de verificación.

Error 8: mantener una sola conexión durante demasiado tiempo

Cuanto más vive una conexión a través de una red móvil, mayor es la probabilidad acumulada de encontrarse con un handover, un cambio de IP o un timeout NAT. Restablecer la conexión periódicamente no es una debilidad, sino una higiene razonable. No temas recrear conexiones.

Herramientas y recursos para diagnóstico y construcción de robustez

La herramienta adecuada ahorra horas. Aquí tienes un arsenal que conviene tener a mano.

Diagnóstico de red y paquetes

  • Captura y análisis de tráfico. Una herramienta de captura de paquetes es tu microscopio. Mostrará quién envió RST, si llegó el ICMP fragmentation needed, de qué tamaño son los paquetes que realmente van, y dónde se interrumpe el flujo. Sin ella, diagnosticar MTU y determinar al culpable del corte se convierte en adivinación.
  • Utilidades de verificación de ruta. Las herramientas de traceroute y verificación de MTU ayudan a entender dónde en el camino cae el tamaño del paquete. Hay modos que buscan deliberadamente el tamaño máximo de paquete que pasa, justo lo que necesitas para configurar MSS.
  • Servidores eco de prueba. Un servidor simple que sepa mantener keep-alive y devolver datos de un tamaño dado es indispensable para medir idle timeouts y reproducir problemas con respuestas grandes en condiciones controladas.

Bibliotecas y enfoques para el cliente

  • Clientes HTTP con configuración flexible de sockets. Elige bibliotecas que den acceso a los parámetros de conexión y permitan establecer timeouts y keepalive a bajo nivel.
  • Mecanismos de streaming. El procesamiento en flujo del cuerpo de la respuesta es obligatorio para descargas grandes, no debes mantener toda la respuesta en memoria.
  • Registro de progreso. Un almacenamiento simple del estado (qué fragmentos se recibieron, cuántos bytes hay en el disco) convierte la reanudación en una tarea trivial.

Calidad de la infraestructura de proxy

Subrayemos por separado: una parte significativa de los problemas de transporte se resuelve del lado de un proveedor de proxies de calidad. Un MSS clamping bien configurado, idle timeouts adecuados, estabilidad en la retención de IP durante el handover y un trabajo transparente con keep-alive son señales de un servicio maduro. Servicios como MobileProxy.space diseñan su infraestructura teniendo en cuenta estos matices de transporte, lo que quita parte del dolor de cabeza al cliente. Pero incluso con un proxy ideal, el cliente debe ser robusto, porque el canal de radio es impredecible por naturaleza.

Casos y resultados: cómo funciona en la práctica

Analicemos varios casos generalizados que reflejan situaciones típicas y sus soluciones. Las cifras son promedio, pero reflejan los órdenes de magnitud reales.

Caso 1: la subida del catálogo se congelaba a la mitad

Situación. Un equipo subía un catálogo grande de productos a través de un proxy móvil en una sola solicitud. La respuesta era de unos 15 megabytes. Se congelaba de forma estable alrededor de los 6-8 megabytes, sin error, solo silencio, hasta que el timeout general se activaba a los pocos minutos.

Diagnóstico. Las solicitudes pequeñas pasaban perfectamente. La captura de tráfico mostró que los paquetes grandes salían con el flag Don't Fragment, y el ICMP fragmentation needed no regresaba. Clásico agujero negro de PMTUD.

Solución. Configuramos MSS clamping con un valor que garantizara que el paquete estuviera dentro del MTU real de la red móvil, y además dividimos la subida en solicitudes paginadas de 2 megabytes.

Resultado. Los congelamientos desaparecieron por completo. El tiempo de subida se volvió predecible y, ante cortes poco frecuentes, solo se repetía una página, no todo el catálogo. La tasa de subidas exitosas pasó de alrededor del 40 por ciento a prácticamente el 100 por ciento.

Caso 2: la conexión moría en las pausas de espera

Situación. El cliente enviaba una solicitud para generar un informe pesado, el servidor tardaba 90-120 segundos en pensar y luego debía devolver el resultado. Pero la conexión ya estaba muerta cuando el informe estaba listo.

Diagnóstico. La medición del idle timeout mostró un corte estable alrededor de los 60 segundos de inactividad. El culpable era el timeout NAT del operador: durante la espera no había tráfico en absoluto.

Solución. Activamos TCP keepalive con idle 20 segundos e interval 10. Ahora, durante la espera, la conexión se refrescaba con sondas de servicio cada 20 segundos.

Resultado. La conexión sobrevivía hasta que el informe estuviera listo. Los cortes en las pausas cesaron. Un beneficio adicional: el cliente comenzó a enterarse en 45-60 segundos de las conexiones realmente muertas, en lugar de colgarse durante minutos.

Caso 3: cortes aleatorios durante el scraping activo

Situación. Sesiones de scraping de larga duración se cortaban caóticamente, sin relación con el volumen o el tiempo. Especialmente frecuente en ciertas horas.

Diagnóstico. Las señales apuntaban a un cambio de IP durante el handover. Después del corte, la nueva conexión se levantaba al instante: la red estaba viva.

Solución. Rediseñamos el cliente bajo la filosofía de conexiones efímeras: transacciones cortas idempotentes, registro de progreso, restablecimiento rápido de la conexión ante cortes, reintentos limitados con pausa corta.

Resultado. Los cortes no desaparecieron físicamente (no se puede vencer el cambio de IP), pero dejaron de ser un problema. Cada corte costaba repetir una transacción corta: fracciones de segundo. La fiabilidad general del proceso mejoró drásticamente y los ingenieros dejaron de pasar noches en vela mirando los registros.

Conclusión general de los casos

Observa el patrón: en cada caso, la solución no estaba en cambiar de proxy, sino en entender el nivel de transporte y configurar correctamente el cliente. El diagnóstico por síntomas nos llevó al culpable concreto, y el tratamiento fue puntual y rápido.

FAQ: preguntas frecuentes sobre cortes de conexión en redes móviles

¿Por qué a través de un proxy móvil las solicitudes cortas funcionan y las largas se cortan?

Porque las operaciones largas viven más tiempo y alcanzan a encontrarse con cada uno de los problemas de transporte: idle timeout en las pausas, límite de tamaño de respuesta, límite de tiempo de vida de la conexión, handover con cambio de IP y agujero negro de PMTUD en paquetes grandes. Las solicitudes cortas se completan antes de que cualquiera de estas cosas se active. La solución es configurar keepalive, MSS e implementar reanudación por Range.

¿Cómo saber quién exactamente corta la conexión: el proxy, el operador o el servidor?

Mira la firma. Cortes en las pausas: timeout NAT del operador o idle del proxy. Corte en un volumen fijo: límite de tamaño del proxy o del servidor. Corte en un tiempo fijo: límite de tiempo de vida. Congelamiento en respuestas grandes: MTU y PMTUD. Cortes aleatorios en movimiento: handover. Para una respuesta precisa, usa captura de tráfico: te mostrará desde qué dirección llegó el RST y si llegó el ICMP.

¿Cuál es la diferencia entre TCP keepalive y HTTP keep-alive?

HTTP keep-alive trabaja a nivel de aplicación y permite reutilizar una sola conexión para múltiples solicitudes, ahorrando en el establecimiento. TCP keepalive trabaja a nivel del núcleo del SO y envía periódicamente sondas de servicio, manteniendo la conexión viva en las pausas y refrescando el NAT. El primero ahorra tiempo en solicitudes nuevas; el segundo salva de cortes por inactividad. Se necesitan ambos.

¿Qué valores de TCP keepalive debo establecer para redes móviles?

Punto de partida: idle 15-25 segundos, interval 10-15 segundos, count 3-4. Esta combinación se adelanta a los timeouts NAT agresivos de los operadores y al mismo tiempo garantiza una detección rápida de conexiones muertas. Los valores exactos se ajustan midiendo tu idle timeout en el operador y proxy concretos.

¿Qué es el agujero negro de PMTUD y cómo reconocerlo?

Es una situación en la que un nodo en el camino descarta paquetes demasiado grandes, pero su notificación ICMP de que es necesario reducir el tamaño es bloqueada y no llega al remitente. Como resultado, los paquetes grandes se pierden infinitamente y los pequeños pasan. Se reconoce por el síntoma: el apretón de manos y las respuestas pequeñas funcionan, pero las respuestas grandes se congelan en seco. Se trata con MSS clamping o reducción del MTU.

¿Se puede mantener una conexión TCP al cambiar de estación base?

Si la red mantiene la misma IP externa durante el handover, sí, la conexión sobrevivirá al cambio, quizás con un pequeño retraso. Si la IP cambia, no, la conexión TCP anterior muere irreversiblemente, porque TCP está rígidamente ligado al par de direcciones y puertos. La estrategia correcta no es intentar conservar la conexión a toda costa, sino construir un cliente que restablezca la conexión sin problemas y continúe desde donde se cortó.

¿Cómo reanudar correctamente un archivo después de un corte?

Verifica si el servidor soporta Range (cabecera Accept-Ranges: bytes y código 206 en la solicitud Range). Luego, ante un corte, averigua el tamaño de la parte ya recibida y solicita con la cabecera Range el resto desde ese byte, añadiendo al mismo archivo. Repite hasta obtenerlo completo, limitando el número de intentos. Al final, compara el tamaño final con el esperado.

¿Qué timeouts debo establecer en el cliente HTTP para un proxy móvil?

Separa el timeout de establecimiento de conexión (unos 10 segundos) y el timeout de inactividad de lectura (unos 30 segundos, como pausa entre porciones de datos, no para toda la transferencia). El presupuesto total de la operación se controla a nivel de la lógica de reanudación, no con un solo timeout enorme. Nunca dejes timeouts ilimitados.

¿Necesito cambiar algo si ya tengo estrategias de reintento por códigos de respuesta?

Sí. Los reintentos por códigos de respuesta (por ejemplo, manejo de 429 con backoff exponencial) son el nivel de aplicación, un tema aparte. Los cortes de transporte (RST, timeouts de lectura, sockets muertos) requieren su propia lógica: reintentos rápidos con pausa corta, reanudación, keepalive y MSS correcto. Estas dos capas no se reemplazan mutuamente y deben coexistir en el cliente.

¿Influye la elección del proveedor de proxy en la robustez del transporte?

Muchísimo. Un proveedor maduro configura MSS clamping, idle timeouts adecuados, intenta mantener la IP durante el handover y trabaja correctamente con keep-alive. Esto elimina parte de los problemas antes de tu código. Pero incluso un proxy ideal no elimina la necesidad de construir un cliente robusto, porque el canal de radio es impredecible por naturaleza.

Conclusión: la conexión es efímera, pero los datos deben llegar

Hemos recorrido el camino desde el síntoma hasta un cliente robusto. Fijemos lo principal para que este artículo se convierta en tu referencia.

Primero. El corte de una conexión larga a través de un proxy móvil no es mística, sino la consecuencia de mecanismos concretos: idle timeouts en tres niveles, límites de tamaño y tiempo, problemas de MTU y handover. Cada uno tiene su firma, y el diagnóstico por síntomas casi siempre lleva al culpable.

Segundo. El timeout más corto en el camino decide el destino de la conexión. Mídelo y configura TCP keepalive más agresivo que ese límite: idle 15-25 segundos para redes móviles. No olvides que TCP keepalive y HTTP keep-alive son mecanismos diferentes y se necesitan ambos.

Tercero. Si las respuestas grandes se congelan y las pequeñas pasan, casi con seguridad es un agujero negro de PMTUD. Se trata con MSS clamping y MTU correcto. No pierdas semanas probando timeouts, revisa el tamaño del paquete primero.

Cuarto. El cambio de IP durante un handover no se puede vencer, pero se puede hacer que no asuste. Construye el cliente en torno a la efimeridad de la conexión: transacciones cortas idempotentes, reanudación por Range, registro de progreso, restablecimiento rápido, reintentos limitados y verificación obligatoria de integridad.

Tus próximos pasos son simples. Mide el idle timeout en tu proxy y operador. Activa y configura TCP keepalive. Verifica el comportamiento con respuestas grandes y, si es necesario, configura MSS. Implementa la reanudación por Range. Y asegúrate de estudiar el material relacionado sobre estrategias de reintento a nivel de códigos de respuesta (429 y backoff exponencial) para cubrir también la capa de aplicación, no solo la de transporte.

La red móvil es impredecible por naturaleza. Pero un cliente construido con respeto a su naturaleza convierte esa impredecibilidad de una fuente de guardias nocturnas en un ruido de fondo rutinario. La conexión es efímera, y eso es normal. Lo importante es que los datos lleguen de todos modos. Ahora sabes cómo lograrlo.