Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

`connect()` blocks for ~21 seconds on Windows when server is unreachable (SYN dropped)

Aperta Adatta ai principianti
#259 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
78/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
c

Direzione di ricerca

Inizia da src/bsd.c, quindi segui bsd_create_socket e bsd_create_connect_socket per confermare quando viene eseguita connect() rispetto alla configurazione della modalità non bloccante. Convalida su Windows 10 o 11 collegandoti a un server irraggiungibile e verificando che il ciclo degli eventi rimanga reattivo; il comportamento su Linux deve rimanere invariato.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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:

  1. SOCK_NONBLOCK is not defined — the socket() call creates a blocking socket.
  2. The original bsd_set_nonblocking on Windows was either missing or did not actually set the socket to non-blocking mode before connect() was called.
  3. 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 after connect() has already been invoked on a blocking socket.
  4. When connect() is called on a blocking socket and the server is unreachable (SYN silently dropped by a firewall), Windows blocks inside connect() 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_NONBLOCK at socket creation time.
    • macOS/BSD also support SOCK_NONBLOCK.
    • Windows has no equivalent flag for socket().

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
  1. ioctlsocket(fd, FIONBIO, &nonblocking) is the Windows API to set a socket to non-blocking mode (equivalent to fcntl(..., O_NONBLOCK) on POSIX systems).
  2. The call is placed in bsd_set_nonblocking, which is called from bsd_create_socket before bsd_create_connect_socket calls connect().
  3. With a non-blocking socket, connect() returns immediately with WSAEWOULDBLOCK (equivalent to EINPROGRESS on POSIX). The actual connection happens asynchronously.
  4. 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 the on_open / on_connect_error callback.

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.

Lingua principale
C
Stelle
1.5k
Fork
310
Merge medio
1h 12m
PR unite (30g)
1

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di uNetworking/uSockets

Tutte le issue di uNetworking/uSockets

Issue simili

Altre issue su C

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.