Fundamentals 9 min read

Why TCP Congestion Control Adds Unexpected RTTs and How to Diagnose It

The article analyzes how TCP's three‑way handshake, slow‑start, and initial congestion window can introduce extra round‑trip times, causing service latency to far exceed network RTT, and shows practical packet captures, Linux defaults, and mitigation techniques.

Liangxu Linux
Liangxu Linux
Liangxu Linux
Why TCP Congestion Control Adds Unexpected RTTs and How to Diagnose It

Problem Scenario

In a low‑latency environment (<1 ms network RTT, 2 ms service latency) a migration to a new network with 100 ms RTT caused observed service latency to rise to 300 ms–400 ms, far higher than the expected ~102 ms.

TCP Handshake Overhead

The three‑way handshake itself adds one RTT. The packet exchange is:

+0   A → B SYN
+0.5RTT B → A SYN+ACK
+1   A → B ACK
+1   A → B Data

Since the client can start sending data after the ACK, only one additional RTT is incurred for connection establishment.

Slow Start and Initial Congestion Window

After the connection is established, TCP does not send all data at once. It sends a limited amount determined by the initial congestion window (cwnd) to avoid overwhelming the network. Linux defaults to TCP_INIT_CWND = 10, meaning up to 10 × MSS (≈14 KB) can be sent in the first RTT. #define TCP_INIT_CWND 10 If the payload is ≤ 14 480 bytes, the transfer completes in two RTTs (one for the handshake, one for data). Any byte beyond this limit forces an extra RTT.

Experimental Verification

Packet captures in a 100 ms RTT environment show:

Sending exactly 14 480 bytes takes 200 ms (2 RTT).

Sending 14 481 bytes takes 300 ms (3 RTT).

Impact on Service Latency

For a typical request/response of ~30 KB each, both directions exceed the initial cwnd, resulting in four RTTs: one for the handshake, one for request data, one for response data, and an extra RTT for the second slow‑start burst. In a 100 ms RTT network this explains observed latencies around 400 ms.

Mitigation

Using persistent TCP connections (keep‑alive) eliminates the handshake overhead for subsequent requests, reducing the total RTT count.

Discussion on Tuning initcwnd

Some developers attempt to set the initial cwnd via socket options:

setsockopt(fd, IPPROTO_TCP, TCP_CWND, &val, sizeof(val));

However, this approach is controversial. IETF experts argue that initcwnd reflects link characteristics and should be configured by network operators, not applications, because an overly large value can exacerbate congestion and prevent automatic recovery.

Linux allows changing the default via route configuration, e.g., using ip route to set a larger cwnd, which can be verified by observing fewer RTTs in packet captures.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

TCPLinuxNetwork Latencycongestion controlRTTSlow Start
Liangxu Linux
Written by

Liangxu Linux

Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.