A TCP client opens short-lived connections to one server IP and port and closes each one first; what caps its sustained new-connection rate, and how do you compute it?
answer
- only one tuple field can vary
- the side that closes first waits
- 2×MSL per used port
- usable ports divided by hold time
- move the close or widen the tuple
basics
~20 sToward one server socket only the client port varies, and the client, closing first, holds each port in TIME-WAIT for 2×MSL. Ceiling: usable ports divided by that time — 16,384 ports over RFC 9293's 240 s is about 68 per second.
solid answer
~50 sToward one server socket, the protocol, both IP addresses and the server port are fixed, so the **client port** is the only 5-tuple field left to vary — the client can have at most one live or lingering connection per port in its ephemeral range. Because it closes first, the client is the **active closer**, and RFC 9293 says it MUST stay in TIME-WAIT for 2×MSL; with the specification's MSL of 2 minutes that is 240 s. The steady-state ceiling is `usable ports / TIME-WAIT duration`: 16,384 dynamic ports / 240 s ≈ 68 new connections per second, while a stack that shortens TIME-WAIT to 60 s reaches about 273. The protocol-level levers are to reuse connections instead of opening new ones, to let the server close first so TIME-WAIT lands there, or to widen the tuple with more client addresses, server addresses or server ports.
go deeper
Recall that the side which closes first keeps TIME-WAIT, and that a client needs a free source port for every connection to the same server socket.
Walk through the 5-tuple to show only the client port varies, and state RFC 9293's 2×MSL with MSL of 2 minutes versus shorter implementation values.
Do the P/T arithmetic out loud, then pick levers: reuse connections, move the active close to the server, widen the tuple or the range — and reject RST-based shortcuts.
Frame it as a fleet design choice: connection reuse and which tier closes first decide where TIME-WAIT accumulates, which is cheaper to fix in architecture than with per-host tuning.
## Only one field of the tuple can change A TCP connection is identified by the 5-tuple `{protocol, local IP, local port, remote IP, remote port}`, and RFC 6056 (Section 2.2) requires the chosen ephemeral port to keep that tuple unique. For one client talking to one server socket: | Tuple field | Value | Varies? | |---|---|---| | protocol | TCP | no | | client IP | the client's one address | no | | server IP | the server's one address | no | | server port | the service port, e.g. `443` | no | | client port | chosen from the ephemeral range | **yes** | So the number of connections that can exist at once toward that server socket, live or lingering, is at most the number of client ports the stack will use. ## Who holds TIME-WAIT, and for how long RFC 9293 (Section 3.6.1): "When a connection is closed actively, it MUST linger in the TIME-WAIT state for a time 2xMSL" (MUST-13). The **active closer** — the side that sends the first FIN — is the one that lingers. In this scenario that is the **client**, so each closed connection keeps its client port tied up after the application has forgotten it. The duration has two sources: - **The specification:** RFC 9293 (Section 3.4.2) takes MSL to be 2 minutes, calling it "an engineering choice", so 2×MSL is **240 s**. - **Implementations:** many stacks use a shorter TIME-WAIT; 60 s is a common implementation value. That is an implementation choice, not an RFC value. Why TIME-WAIT exists at all is the connection-teardown topic; here only its cost matters. ## The arithmetic Let **P** be the usable client ports and **T** the time each port is held after close. In steady state, with connections that live much shorter than T: 1. Each new connection consumes one port for about T seconds. 2. At most P ports can be held at once. 3. So the sustained rate is at most **P / T** connections per second. | Ephemeral range | Ports P | TIME-WAIT T | Ceiling P/T | |---|---|---|---| | `49152-65535` (IANA dynamic) | 16,384 | 240 s (RFC 2×MSL) | ≈ 68 /s | | `49152-65535` | 16,384 | 60 s (implementation) | ≈ 273 /s | | `1024-65535` (RFC 6056 suggestion) | 64,512 | 240 s | ≈ 269 /s | Connections that are still open also hold ports, so with C concurrent live connections the budget is roughly `C + rate × T ≤ P`. When the budget is exceeded, new connects fail locally because no port yields a unique tuple. ## Levers at the protocol level - **Open fewer connections.** Reusing a connection for many requests removes the per-request port cost entirely; pooling policy belongs to the HTTP and connection-management topics. - **Let the server close first.** Then the server becomes the active closer and holds TIME-WAIT. RFC 8085 (Section 3) notes that applications "can shift maintenance of the TIME-WAIT state" by controlling which end closes. The server's TIME-WAIT records all share its one service port and differ by client socket, so they cost the server memory, not port numbers. - **Widen the tuple.** Extra client source addresses, extra server addresses or extra server ports each create a separate tuple space. This helps only if the stack reuses a local port per destination: RFC 6056's Algorithm 3 selects per destination endpoint, while an allocator that treats every ephemeral port as globally in use gains no port capacity from more destinations. - **Widen the range.** Following RFC 6056's `1024-65535` suggestion roughly quadruples P, minus ports local services need. ## Reopening from TIME-WAIT, and what not to do RFC 9293 (MAY-2) lets the side holding TIME-WAIT **accept** a new SYN from the same remote socket and reopen the connection, provided its new initial sequence number is larger than the largest sequence number it used on the previous incarnation, and it returns to TIME-WAIT if the SYN proves to be an old duplicate. RFC 6191, a Best Current Practice, improves this using timestamps. Both help when a server holds TIME-WAIT and a client reuses a port. They do not describe a closing client reusing its *own* TIME-WAIT port for an outgoing SYN; that is an implementation extension outside the RFCs. Two tempting shortcuts are defects: - **Aborting with RST to skip TIME-WAIT** discards unsent data and removes the protection against old duplicates. - **Reusing a tuple the server still holds** risks a failed handshake or TIME-WAIT assassination (RFC 1337), as RFC 6056 Section 2.3 warns.
- If the server closes first instead, does the server run out of ports?No. The server's TIME-WAIT records all share its one service port and differ by client address and port, so they cost memory, not port numbers, and the client's port is free once its side of the close completes. The residual risk is a client reusing a port whose tuple the server still holds; RFC 9293 lets the server accept such a SYN under conditions on sequence numbers, and RFC 6191 makes that more reliable with timestamps.
- Does spreading connections across several server addresses raise the client's ceiling?It can. Each (server address, server port) pair is a separate tuple space, so the same client port may be in use toward each at once — if the stack allocates ports per destination, as RFC 6056's hash-based algorithms do. A stack that treats every ephemeral port as globally in use gains no port capacity from extra destinations, only from a wider range or more client addresses.
saying these in an interview costs you the question
- TIME-WAIT always sits on the server, whichever side closed first.
- The RFC fixes TIME-WAIT at 60 seconds.
- A client can open 65,535 new connections per second to one server.
- More server worker threads will fix client ephemeral-port exhaustion.
- Closing with RST instead of FIN is a safe way to avoid TIME-WAIT.