`connect()` blocks for ~21 seconds on Windows when server is unreachable (SYN dropped)
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 78/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- c
- Área
- networking, operating-systems
Línea de trabajo
Comienza en src/bsd.c y luego sigue bsd_create_socket y bsd_create_connect_socket para confirmar cuándo se ejecuta connect() en relación con la configuración del modo no bloqueante. Valídalo en Windows 10 u 11 conectándote a un servidor inalcanzable y comprobando que el bucle de eventos siga respondiendo; el comportamiento en Linux debe permanecer sin cambios.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Problem Background
When a TCP client attempts to connect to a server that is unreachable (e.g., firewall silently drops SYN packets, or server is down and no RST is returned), the connect() call on a blocking socket on Windows blocks for approximately 21 seconds (the default TCP SYN retransmission timeout: 3s + 6s + 12s exponential backoff).
In uSockets with the libuv backend (LIBUS_USE_LIBUV), this causes the entire event loop to freeze for 21 seconds, making the application completely unresponsive. No timers fire, no other I/O is processed, and no callbacks are invoked during this period.
Root Cause
The issue is in the interaction between bsd_create_socket, bsd_set_nonblocking, and the call order of connect() on Windows.
In the original code, bsd_create_socket creates a socket and then calls bsd_set_nonblocking:
// src/bsd.c
LIBUS_SOCKET_DESCRIPTOR bsd_create_socket(int domain, int type, int protocol) {
int flags = 0;
#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
flags = SOCK_CLOEXEC | SOCK_NONBLOCK;
#endif
LIBUS_SOCKET_DESCRIPTOR created_fd = socket(domain, type | flags, protocol);
return bsd_set_nonblocking(apple_no_sigpipe(created_fd));
}
On Linux, SOCK_NONBLOCK is defined, so the socket is created as non-blocking directly in the socket() syscall. The bsd_set_nonblocking call is a safety net.
On Windows, however:
SOCK_NONBLOCKis not defined — thesocket()call creates a blocking socket.- The original
bsd_set_nonblockingon Windows was either missing or did not actually set the socket to non-blocking mode beforeconnect()was called. - The original code implicitly relied on libuv's
uv_poll_init_socket()to set the socket to non-blocking mode. However,uv_poll_init_socket()is called afterconnect()has already been invoked on a blocking socket. - When
connect()is called on a blocking socket and the server is unreachable (SYN silently dropped by a firewall), Windows blocks insideconnect()for the full TCP SYN timeout period (~21 seconds).
Call sequence (before fix):
bsd_create_socket()
→ socket() // creates BLOCKING socket on Windows (no SOCK_NONBLOCK)
→ bsd_set_nonblocking() // either missing or ineffective on Windows
→ returns fd
bsd_create_connect_socket()
→ bsd_create_socket()
→ connect(fd, ...) // BLOCKS for ~21 seconds on Windows when SYN is dropped!
// Event loop is completely frozen during this time
→ returns fd
us_socket_context_connect()
→ bsd_create_connect_socket()
→ us_poll_init() // registers with libuv
→ us_poll_start() // uv_poll_start() → uv_poll_init_socket() sets non-blocking
// BUT TOO LATE — connect() has already blocked for 21s
Wireshark Evidence
When the server is unreachable and the firewall drops SYN packets, the Windows TCP stack retransmits SYN at the following intervals:
T+0.0s [SYN] (initial SYN)
T+3.0s [SYN] (1st retransmit)
T+9.0s [SYN] (2nd retransmit)
T+21.0s connect() finally returns with WSAETIMEDOUT
During this entire 21-second period, the uSockets event loop is completely blocked — no timers, no I/O, no callbacks can be processed.
Impact
- Application becomes completely unresponsive for ~21 seconds when connecting to an unreachable server on Windows.
- Connection retry logic (e.g., 5-second reconnect timers) is completely blocked — they can only fire after the 21-second blocking
connect()returns. - The problem is Windows-specific because:
- Linux uses
SOCK_NONBLOCKat socket creation time. - macOS/BSD also support
SOCK_NONBLOCK. - Windows has no equivalent flag for
socket().
- Linux uses
Solution
Set the socket to non-blocking mode immediately after creation, before connect() is called, using ioctlsocket(fd, FIONBIO, &nonblocking) on Windows.
Patch
In src/bsd.c, fix bsd_set_nonblocking to actually set non-blocking mode on Windows:
LIBUS_SOCKET_DESCRIPTOR bsd_set_nonblocking(LIBUS_SOCKET_DESCRIPTOR fd) {
#ifdef _WIN32
- /* Empty or ineffective implementation */
- /* Previously relied on libuv's uv_poll_init_socket to set non-blocking,
- * but that happens AFTER connect() has already blocked for 21 seconds. */
+ /* WARNING: Libuv's uv_poll_init_socket does set the socket to non-blocking,
+ * but only AFTER connect() has already been called on a blocking socket.
+ * On Windows, connect() on a blocking socket blocks for ~21 seconds when
+ * the server is unreachable (firewall drops SYN), freezing the entire
+ * event loop. We must set non-blocking BEFORE connect() is called. */
+ unsigned long nonblocking = 1;
+ ioctlsocket(fd, FIONBIO, &nonblocking);
#else
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
#endif
How It Works
ioctlsocket(fd, FIONBIO, &nonblocking)is the Windows API to set a socket to non-blocking mode (equivalent tofcntl(..., O_NONBLOCK)on POSIX systems).- The call is placed in
bsd_set_nonblocking, which is called frombsd_create_socketbeforebsd_create_connect_socketcallsconnect(). - With a non-blocking socket,
connect()returns immediately withWSAEWOULDBLOCK(equivalent toEINPROGRESSon POSIX). The actual connection happens asynchronously. - libuv monitors the socket for writability and reports completion (or error) via the event loop — without blocking.
Call sequence (after fix):
bsd_create_socket()
→ socket() // creates BLOCKING socket on Windows
→ bsd_set_nonblocking() // NOW sets non-blocking via ioctlsocket(FIONBIO) ← FIX
→ returns NON-BLOCKING fd
bsd_create_connect_socket()
→ bsd_create_socket()
→ connect(fd, ...) // returns immediately with WSAEWOULDBLOCK ← NO MORE 21s BLOCK
→ returns fd
us_socket_context_connect()
→ bsd_create_connect_socket()
→ us_poll_init()
→ us_poll_start() // libuv monitors socket asynchronously
// Event loop continues running — timers, I/O all work
Testing
Tested on Windows 10/11:
- Before fix: Connecting to an unreachable server (SYN dropped by firewall) blocks
connect()for ~21 seconds. Event loop is frozen. No timers fire. Application appears hung. - After fix:
connect()returns immediately (WSAEWOULDBLOCK). Event loop continues running. Connection result (success or timeout) is reported asynchronously via theon_open/on_connect_errorcallback.
Also verified on Linux: Behavior is unchanged — SOCK_NONBLOCK at socket creation already ensures non-blocking behavior, and fcntl(F_SETFL, O_NONBLOCK) in bsd_set_nonblocking provides the safety net.
- Lenguaje dominante
- C
- Estrellas
- 1.5k
- Forks
- 307
- Métricas de merge de PR
- Sin PR fusionados en 30 d
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de uNetworking/uSockets
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
uNetworking/uSockets#258 ·
-
udp_benchmark can work on mac osAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
uNetworking/uSockets#256 · 1 reacción ·
-
enhancement help wanted
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
uNetworking/uSockets#252 · 5 comentarios ·
-
Dificultad 3/5 1-2 días Aptitud para principiantes 45/100
uNetworking/uSockets#255 · 2 comentarios · 1 reacción ·
-
Dificultad 4/5 3-5 días Aptitud para principiantes 38/100
uNetworking/uSockets#250 ·
Todos los issues de uNetworking/uSockets
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
darktable-org/darktable#22455 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
area:ci kind:gate-defect
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
InauguralSystems/EigenScript#1448 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
BasedHardware/omi#20084 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 64/100
raspberrypi/pico-sdk#3225 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100