Tenía dos sesiones abiertas al mismo tiempo hacia el mismo servidor.
Una era completamente fluida — el mouse respondía, las cámaras
cargaban, podía navegar entre vistas sin problema. La otra se sentía
como conectarse desde un cibercafé con mala señal a las 11 de la
noche: el cursor tardaba, las imágenes llegaban por bloques, y cada
tanto la sesión simplemente se caía y volvía a conectarse sola.
Lo raro era que las dos apuntaban exactamente al mismo destino. El
mismo servidor, la misma IP, el mismo puerto. La única diferencia
visible era el camino: una llegaba desde una laptop conectada
directamente al switch del segmento de planta, la otra venía desde
una estación en la red corporativa cruzando un firewall.
Mi primer instinto fue buscar latencia o ancho de banda insuficiente.
Pero los números no cuadraban — la prueba de conectividad TCP no
mostraba nada que justificara esa diferencia visual tan marcada. Así
que fui un nivel más abajo, y lo que encontré en los logs del servidor
me pareció lo suficientemente útil como para documentarlo.
Este post es la historia de cómo llegué a la causa raíz de una sesión
RDP lenta, qué evidencia encontré en los registros del sistema, y por
qué el problema no tenía nada que ver con la calidad de la red.
Es un caso real de mi trabajo, anonimizado donde corresponde — los
nombres de los servidores y equipos son ficticios, pero el incidente
y los pasos son tal cual los viví.
Resumen rápido
| Sistema afectado | Servidor de visualización de cámaras (acceso por RDP) |
| Síntoma | Sesión lenta, imagen degradada, desconexiones frecuentes |
| Desde dónde | Estación en red corporativa, cruzando firewall |
| Causa raíz | Firewall bloqueaba UDP 3389 — RDP operaba solo en modo TCP |
| Factor agravante | Servidor sin detección automática de red activada |
| Solución | Regla UDP 3389 agregada al firewall, origen y destino específicos |
| Resultado | Sesión estable, canal gráfico RemoteFX activo |
Qué es RDP y por qué el protocolo importa aquí
Si trabajás en sistemas industriales, probablemente usás RDP todos
los días sin pensar mucho en él. Pero para entender por qué este
problema pasó, vale la pena detenerse un segundo en cómo funciona
por dentro.
RDP (Remote Desktop Protocol) es la tecnología de Microsoft que
permite controlar un computador a distancia, como si estuvieras
sentado frente a él. Lo que ves en tu pantalla local es la pantalla
remota, capturada, comprimida y enviada por red en tiempo real.
Para eso, RDP puede usar dos transportes distintos: TCP y UDP.
TCP es el carril convencional. Garantiza que cada paquete llegue
completo y en orden, reenvía los que se pierden, y hace todo eso a
cambio de un poco más de latencia. Es confiable pero no está
optimizado para video en tiempo real.
UDP es el carril exprés. No garantiza entrega ni orden — si un paquete
se pierde, no lo reenvía, simplemente sigue adelante. Para video en
tiempo real eso es exactamente lo que querés: preferís perder un frame
que esperar a que llegue tarde y arruinar la fluidez. Las videollamadas
de Teams, las cámaras de seguridad, los juegos online — todos usan UDP
por esta razón.
Cuando ambos carriles están disponibles, RDP activa automáticamente
lo que llama RemoteFX multitransport: usa UDP para el canal gráfico
y TCP para el resto. La sesión es fluida. Si UDP no está disponible —
porque algo en el camino lo bloquea — RDP cae de vuelta a TCP puro
para todo. La sesión sigue funcionando, pero la calidad visual se
degrada notablemente, sobre todo cuando hay movimiento de pantalla
intenso como el de un sistema de cámaras en tiempo real.
Eso era exactamente lo que estaba pasando.
Timeline de referencia rápida
| Hora | Evento |
|---|---|
| Inicio | Reporte de lentitud en la sesión RDP desde la red corporativa |
| +10 min | Prueba TCP al puerto 3389: timeout de 21 segundos |
| +20 min | Se confirma que la sesión sí conecta pero la calidad es mala |
| +35 min | Se extrae log RdpCoreTS — errores TCP 995 y reconexiones en cadena |
| +50 min | Se filtra log por IP de cada equipo — se detecta diferencia de protocolo |
| +60 min | Laptop local: TCP + UDP confirmado. Estación corporativa: solo TCP |
| +75 min | Se revisan reglas del firewall — no existe regla UDP 3389 |
| +90 min | Se agrega regla UDP 3389 (origen específico → destino específico) |
| +95 min | Sesión estable, canal gráfico activo, imagen fluida |
Nota: horarios aproximados y reconstruidos de memoria.
Lo primero que intenté (y dónde choqué)
Lo más lógico cuando una sesión remota está lenta es medir la
latencia. Para eso usé PowerShell directamente desde la estación
corporativa: intentar abrir una conexión TCP al puerto 3389 del
servidor de destino y medir cuánto tarda en responder.
$ip = "SERVIDOR-DESTINO"
1..10 | ForEach-Object {
$sw = [System.Diagnostics.Stopwatch]::StartNew()
$tcp = New-Object Net.Sockets.TcpClient
try {
$tcp.Connect($ip, 3389)
$sw.Stop()
Write-Host "Intento $_`: $($sw.ElapsedMilliseconds) ms"
} catch {
Write-Host "Intento $_`: FALLO ($($sw.ElapsedMilliseconds) ms)"
} finally {
$tcp.Close()
}
Start-Sleep -Milliseconds 500
}
Lo que obtuve fue esto:
Exception calling "Connect" with "2" argument(s): "A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 10.X.X.X:3389"
Intento 1: 21127 ms
Veintiún segundos de timeout. Eso parecía confirmar que el puerto
estaba bloqueado. Pero entonces, ¿cómo era posible que la sesión
RDP sí estuviera activa y conectada?
La respuesta es que el puerto no estaba bloqueado para TCP — estaba
bloqueado para UDP. La sesión RDP conectaba usando TCP, que sí estaba
permitido. Mi prueba fallaba porque intentaba abrir una nueva conexión
TCP directa desde el contexto del script, y eso tomaba otro camino en
el firewall. La sesión ya establecida había negociado su ruta de otra
manera.
Así que me quedé sin métrica directa de latencia y tuve que buscar
la evidencia en otro lado.
La evidencia estaba en el servidor
Dentro de la sesión RDP al servidor, corrí este comando en PowerShell
para extraer el log operacional del servicio de escritorio remoto —
el RdpCoreTS. Este log registra cada conexión: qué equipo se conectó,
qué protocolo negoció, cuánto tardó en mostrar la primera imagen, y
si hubo errores.
Get-WinEvent -LogName `
"Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational" |
Select-Object TimeCreated, Message |
Format-List
Lo primero que encontré fue esto, repetido cientos de veces en un
intervalo de segundos:
TimeCreated : [FECHA]
Message : TCP socket WRITE operation failed, error 995
TimeCreated : [FECHA]
Message : TCP socket WRITE operation failed, error 64
El error 995 en Windows corresponde a WSA_OPERATION_ABORTED — una
operación de red fue cancelada antes de completarse. El error 64 es
ERROR_NETNAME_DELETED — la ruta de red desapareció abruptamente.
Cientos de estos en el mismo segundo son la firma de una sesión que
se estaba cayendo a pedazos.
También encontré esta línea que aparecía en cada conexión:
TimeCreated : [FECHA]
Message : The network characteristics detection function has been
disabled because of Reason Code: 2 (Server Configuration).
El servidor tenía desactivada la función que ajusta automáticamente
la calidad del canal gráfico según las condiciones del enlace. Lo
anoté para después.
Y esto, que aparecía en todas las reconexiones de la estación
corporativa:
TimeCreated : [FECHA]
Message : The multi-transport connection finished for tunnel: 1,
its transport type set to TCP: Reason Code: 3
(No UDP Packets Received).
Razón 3: no llegaron paquetes UDP. No era una suposición — era el
servidor diciendo textualmente que intentó establecer el canal UDP y
nunca recibió respuesta.
Comparar sesiones por IP de origen
Para poder comparar la sesión de la laptop local con la de la estación
corporativa, había que filtrar el log por la IP de cada una. Sin eso,
los registros de ambas aparecen mezclados y es imposible ver el patrón.
Get-WinEvent -LogName `
"Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational" |
Where-Object {
$_.Message -like "*10.X.X.AAA*" -or
$_.Message -like "*10.X.X.BBB*" -or
$_.Message -like "*TMT*"
} |
Select-Object TimeCreated, Message |
Format-List
El output que importaba era este. De la estación corporativa:
TimeCreated : [FECHA] [HORA-A]
Message : TMT: ConnectionName=RDP-Tcp#62, PromptForCredentials=125,
PromptForCredentialsDone=14906, GfxChannelOpened=16218,
FirstGraphicsReceived=16453 [ms]
TimeCreated : [FECHA] [HORA-A]
Message : The server accepted a new TCP connection from client
10.X.X.AAA:XXXXX.
TimeCreated : [FECHA] [HORA-B]
Message : TMT: ConnectionName=RDP-Tcp#74, PromptForCredentials=203,
PromptForCredentialsDone=3062, GfxChannelOpened=4562,
FirstGraphicsReceived=4765 [ms]
TimeCreated : [FECHA] [HORA-B]
Message : The server accepted a new TCP connection from client
10.X.X.AAA:XXXXX.
Y de la laptop local:
TimeCreated : [FECHA] [HORA-C]
Message : The server accepted a new UDP connection from client
[10.X.X.BBB]:XXXXX.
TimeCreated : [FECHA] [HORA-C]
Message : The server accepted a new TCP connection from client
10.X.X.BBB:XXXXX.
Ahí estaba todo. La laptop generaba una línea de conexión TCP y
una de conexión UDP. La estación corporativa generaba solo la de TCP,
en todas sus sesiones, sin excepción.
Y los tiempos FirstGraphicsReceived de la estación corporativa
oscilaban entre 4.765 ms y 16.453 ms — entre cinco y dieciséis
segundos hasta ver la primera imagen en pantalla. La variabilidad
entre ambos extremos también explicaba algo que habían reportado
antes: que el problema era intermitente. No siempre igual de lento,
pero nunca realmente fluido.
El firewall, confirmado
Con esa evidencia en mano, pedí que revisáramos las reglas del
firewall corporativo — el que está entre la red de la estación y el
segmento de planta donde vive el servidor.
Las reglas para ese servidor eran prolijas. Había una entrada
específica para la estación corporativa: protocolo TCP, puerto de
destino 3389, acción accept. Exactamente lo necesario para que RDP
conecte. Pero no había ninguna regla equivalente para UDP.
| Descripción | Origen | Destino | Protocolo | Puerto | Acción |
|---|---|---|---|---|---|
| ESTACION-A-SERVIDOR | 10.X.X.AAA | 10.Y.Y.YYY | TCP | 3389 | Accept |
| (no existía) | 10.X.X.AAA | 10.Y.Y.YYY | UDP | 3389 | — |
La laptop local no necesitaba pasar por ese firewall — estaba en la
misma subred que el servidor, conectada directamente por switch. Por
eso podía usar UDP sin restricción. La estación corporativa tenía que
cruzar el firewall, y el firewall solo tenía la mitad del permiso:
dejaba pasar el carril convencional pero bloqueaba el exprés.
La solución y lo que quedó pendiente
La corrección fue agregar una regla en el firewall: mismo origen,
mismo destino, misma lógica que la regla TCP existente, pero
protocolo UDP y puerto 3389.
| Descripción | Origen | Destino | Protocolo | Puerto | Acción |
|---|---|---|---|---|---|
| ESTACION-A-SERVIDOR | 10.X.X.AAA | 10.Y.Y.YYY | TCP | 3389 | Accept |
| ESTACION-A-SERVIDOR-UDP | 10.X.X.AAA | 10.Y.Y.YYY | UDP | 3389 | Accept |
Después del cambio, el servidor empezó a registrar conexiones UDP
desde la estación corporativa. El log confirmó el resultado:
TimeCreated : [FECHA]
Message : The server accepted a new UDP connection from client
[10.X.X.AAA]:XXXXX.
TimeCreated : [FECHA]
Message : The server accepted a new TCP connection from client
10.X.X.AAA:XXXXX.
Ambas líneas. Canal gráfico activo. Sesión fluida.
Pero había dos cosas más que el log había mostrado y que quedaban
sin resolver.
La primera: entre las 01:18 y la 01:20 AM del mismo día, la sesión
corporativa había generado seis reconexiones consecutivas en menos
de dos minutos, todas con ERROR_NETNAME_DELETED. Eso no lo causa
UDP bloqueado — es inestabilidad del enlace. Con UDP habilitado la
sesión va a ser más fluida, pero si el enlace sigue teniendo cortes
intermitentes, los cortes van a seguir apareciendo. Quedó pendiente
coordinar con el área de redes la revisión de ese segmento.
La segunda: la función de detección automática de red seguía
desactivada. Activarla requiere una política de grupo en el servidor:
Computer Configuration
→ Administrative Templates
→ Windows Components
→ Remote Desktop Services
→ Remote Desktop Session Host
→ Connections
→ "Select network detect on the server" → Enabled
Con eso activo, RDP puede reducir automáticamente la calidad del
canal gráfico cuando el enlace se degrada, en lugar de intentar
siempre a máxima calidad y saturarlo más. Un cambio chico con
impacto visible en conexiones que cruzan varios saltos de red.
Comandos útiles si te pasó algo parecido
Si tenés una sesión RDP que conecta pero se siente lenta, estos
son los pasos que seguiría ahora, en orden.
1. Verificar si el puerto 3389 responde directamente (desde el equipo cliente)
$ip = "IP-DEL-SERVIDOR"
1..10 | ForEach-Object {
$sw = [System.Diagnostics.Stopwatch]::StartNew()
$tcp = New-Object Net.Sockets.TcpClient
try {
$tcp.Connect($ip, 3389)
$sw.Stop()
Write-Host "Intento $_`: $($sw.ElapsedMilliseconds) ms"
} catch {
Write-Host "Intento $_`: FALLO ($($sw.ElapsedMilliseconds) ms)"
} finally {
$tcp.Close()
}
Start-Sleep -Milliseconds 500
}
Resultado esperado — conexión sana:
Intento 1: 8 ms
Intento 2: 6 ms
Intento 3: 7 ms
Resultado que indica problema:
Intento 1: 21127 ms
Si el timeout aparece pero la sesión RDP sí conecta, el problema
probablemente no es latencia sino protocolo — seguir con el paso 2.
2. Extraer el log de RDP del servidor de destino (desde dentro de la sesión RDP)
Get-WinEvent -LogName `
"Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational" |
Select-Object TimeCreated, Message |
Format-List
Buscar estas líneas específicas:
Señal de UDP bloqueado:
Message : The multi-transport connection finished for tunnel: 1,
its transport type set to TCP: Reason Code: 3
(No UDP Packets Received).
Señal de inestabilidad de enlace:
Message : TCP socket WRITE operation failed, error 995
Message : TCP socket WRITE operation failed, error 64
Señal de servidor sin adaptación automática:
Message : The network characteristics detection function has been
disabled because of Reason Code: 2 (Server Configuration).
3. Comparar qué protocolos acepta el servidor por IP de origen
Reemplazar las IPs con las de los equipos que querés comparar:
Get-WinEvent -LogName `
"Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational" |
Where-Object {
$_.Message -like "*IP-EQUIPO-A*" -or
$_.Message -like "*IP-EQUIPO-B*" -or
$_.Message -like "*TMT*"
} |
Select-Object TimeCreated, Message |
Format-List
Lo que querés ver — canal gráfico activo:
Message : The server accepted a new UDP connection from client
[10.X.X.X]:XXXXX.
Message : The server accepted a new TCP connection from client
10.X.X.X:XXXXX.
Lo que indica UDP bloqueado — solo TCP:
Message : The server accepted a new TCP connection from client
10.X.X.X:XXXXX.
Si un equipo genera ambas líneas y otro solo genera la TCP,
el problema está en lo que hay entre ese equipo y el servidor —
casi siempre un firewall.
4. Verificar tiempos de carga de imagen por sesión
Get-WinEvent -LogName `
"Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational" |
Where-Object { $_.Message -like "*TMT*" } |
Select-Object TimeCreated, Message |
Format-List
Resultado con canal gráfico activo (UDP disponible):
Message : TMT: ConnectionName=RDP-Tcp#XX, PromptForCredentials=210,
PromptForCredentialsDone=1840, GfxChannelOpened=1950,
FirstGraphicsReceived=2100 [ms]
Resultado con canal degradado (solo TCP):
Message : TMT: ConnectionName=RDP-Tcp#XX, PromptForCredentials=125,
PromptForCredentialsDone=14906, GfxChannelOpened=16218,
FirstGraphicsReceived=16453 [ms]
La diferencia entre ~2 segundos y ~16 segundos es la diferencia
entre RemoteFX activo y RDP cayendo de vuelta a TCP puro. Si los
tiempos de distintas sesiones varían mucho entre sí, el enlace
tiene inestabilidad además del problema de protocolo.
Qué me quedó de esto
El RDP que "conecta" no es necesariamente el RDP que funciona bien. Que la sesión establezca conexión dice que TCP está habilitado. No dice nada sobre UDP, que es el protocolo que hace fluido el canal gráfico. Son dos cosas distintas y conviene verificarlas por separado.
Lo aprendí porque mi primera herramienta — medir latencia TCP — me
daba un resultado confuso. La conexión ya existía por otro camino.
Tuve que ir a los logs del servidor para entender qué protocolo
estaba llegando realmente.
Los logs de RdpCoreTS son una fuente de diagnóstico que vale conocer. La línea TMT con
FirstGraphicsReceivedda una métrica concreta de rendimiento por sesión. La presencia o ausencia de la línea de conexión UDP te dice de inmediato si el canal gráfico optimizado está activo. No aparece en ningún dashboard por defecto, pero está en el Event Viewer esperando que alguien la lea.
Filtrar por IP de origen fue lo que permitió comparar. Sin separar los registros por equipo, los logs de ambas sesiones aparecían mezclados y el patrón era invisible. Una vez separados, la diferencia era obvia en dos líneas.
Abrir UDP en un firewall requiere criterio, no paranoia. La preocupación de seguridad es válida: UDP 3389 amplía la superficie de ataque. Pero una regla con origen específico y destino específico reduce ese riesgo drásticamente. Lo que no es razonable es bloquear UDP genéricamente y esperar que RDP funcione bien en todos los escenarios. La decisión tiene que ser consciente y con las restricciones correctas.
Acciones implementadas
- Se agregó regla en el firewall corporativo: UDP 3389, origen y
destino específicos, como complemento a la regla TCP existente. - Se dejó pendiente la activación de detección automática de red
en el servidor vía GPO, para que RDP pueda ajustar la calidad del
canal según las condiciones del enlace. - Se coordinó con el área de redes la revisión del enlace
corporativo, dado el historial de reconexiones frecuentes en el
log — independiente del problema de UDP.
Cierre
Este fue uno de esos casos donde la solución terminó siendo simple,
pero llegar a ella requirió ir a un lugar que no es tan obvio: los
logs del servidor de destino, no los del cliente que se conecta.
Una vez ahí, la evidencia era directa.
Si trabajás con sesiones RDP en entornos industriales y tenés alguna
que "conecta pero se siente lenta", el primer lugar donde buscaría
ahora es RdpCoreTS/Operational en el Event Viewer del servidor.
La respuesta probablemente ya está ahí.
También te puede interesar
Lección de cierre
En sistemas industriales, una señal congelada, una cámara lenta o una interfaz caída rara vez se explican con una sola causa. El valor está en ordenar síntomas, evidencias, cambios recientes y dependencias técnicas antes de intervenir.
Este artículo tiene fines educativos. Los casos pueden estar simplificados o anonimizados y no representan a ninguna empresa, cliente o proveedor.
Member discussion