Before a client can exchange ordinary application bytes over TCP, the two endpoints must agree that a connection exists.
That agreement is created by the TCP three-way handshake:
These three messages do more than test whether a server port is reachable. They establish state at both endpoints, synchronize the starting sequence number for each direction, and advertise selected TCP capabilities.
The handshake is short, but it explains many production symptoms: connection timeouts, refused connections, repeated SYN packets, overloaded listen queues, and the round-trip delay before a new connection can carry ordinary application traffic.
TCP itself does not require one endpoint to be permanently called the client and the other the server. For the usual handshake, however, the roles are convenient.
The server performs a passive open. It creates a socket, associates it with a local address and port, and asks the operating system to listen for incoming connections.
The client performs an active open. It asks its operating system to connect a local socket to the server's address and port.
The listening socket represents a willingness to receive connections. It is not itself the eventual one-to-one data stream. When a handshake succeeds, the server kernel creates connection state for that specific peer while the original listening socket remains available for other clients.
Suppose a client uses this endpoint pair:
The connection is identified by the transport protocol and four endpoint values:
Another client can connect to the same server port because its source address or source port differs. The server can therefore maintain many independent TCP connections through one listening port.
The three messages are named after TCP control flags:
SYN means synchronize. It announces an endpoint's initial sequence number and requests a new connection.
ACK means acknowledgment. When this flag is set, the acknowledgment-number field is valid.
The server combines both flags in its reply, producing SYN-ACK.
The sequence values x and y are selected independently. TCP tracks each direction of the connection separately, so the server does not copy or continue the client's number.
The client considers its connection established after it receives a valid SYN-ACK and sends the final ACK. The server considers the connection established when that final ACK arrives. For a brief period, the two endpoints can therefore report different states.
The client's operating system chooses an available local port if the application has not selected one. It also chooses a 32-bit Initial Sequence Number, or ISN, for the client-to-server byte stream.
Assume the client chooses:
It sends a TCP segment with:
The client then enters the SYN-SENT state and waits for a response.
Initial sequence numbers normally vary between connections. This helps distinguish the bytes of one connection from delayed segments belonging to an older connection that used the same endpoint pair. Unpredictable values also make blind packet injection more difficult than a fixed starting value would.
Although the SYN usually carries no application payload, it consumes one position in TCP's sequence space. The first ordinary client byte will therefore use sequence number 1001, not 1000.
That rule often looks surprising in a packet capture:
The SYN can also carry TCP options. These options advertise capabilities and limits that should be known before ordinary data transfer begins.
When the listening server's TCP implementation processes the valid SYN, it records pending state for the connection and chooses its own initial sequence number.
Assume:
The server replies with:
The acknowledgment number means:
I have received everything through sequence number 1000, and the next sequence position I expect from you is 1001.
TCP acknowledgments normally identify the next expected sequence number, not the last sequence number received. This convention makes the arithmetic around the handshake and data stream consistent.
The SYN-ACK performs two jobs in one segment:
ACK = 1001 confirms the client's SYN and initial sequence number.SYN, Seq = 7000 announces the server's initial sequence number.After sending it, the server enters the SYN-RECEIVED state. It has heard from the client, but it does not yet know whether the client received the server's SYN.
The server's SYN also consumes one sequence position. Its first ordinary byte will use sequence number 7001.
The client validates the SYN-ACK. In particular, the acknowledgment must match what the client expects for its SYN.
It then sends:
The acknowledgment tells the server that its SYN and initial sequence number arrived successfully. As part of processing the valid SYN-ACK, the client enters ESTABLISHED and sends this final ACK. The server enters ESTABLISHED when it receives the ACK.
Unlike SYN, a pure ACK consumes no sequence space. If the final segment carries no data, the client's next segment can still begin at sequence number 1001.
The final ACK is allowed to carry the client's first application bytes. If it carries 50 bytes, for example:
Combining the final acknowledgment with data avoids sending an additional empty segment. The logical handshake is still complete because the ACK field confirms the server's SYN.
Putting the example together:
| Message | Flags | Sequence Number | Acknowledgment Number | Meaning |
|---|---|---|---|---|
| Client to server | SYN | 1000 | Not valid | My initial sequence number is 1000 |
| Server to client | SYN, ACK | 7000 | 1001 | My ISN is 7000; I received your SYN |
| Client to server | ACK | 1001 | 7001 | I received your SYN |
The arithmetic follows two rules:
The two directions remain independent:
Actual sequence numbers wrap after the largest 32-bit value, but packet-analysis tools handle that arithmetic. For reading a handshake, the important task is to confirm that each acknowledgment advances correctly past the peer's SYN.
Wireshark commonly displays relative sequence numbers to make a capture easier to read. It may show the first SYN as Seq=0, the SYN-ACK as Ack=1, and the final ACK as Seq=1, Ack=1. Those are normalized display values; the actual initial sequence numbers on the wire are normally not zero.
Loading simulation...
Each endpoint needs to accomplish two things:
The first SYN announces the client's sequence number. The SYN-ACK both acknowledges the client's number and announces the server's number. The final ACK confirms that the client received the server's number.
Two messages would leave the server uncertain:
At this point, the client knows that the server received its SYN. The server does not know whether the SYN-ACK reached the client. It cannot distinguish a working two-way exchange from a client that disappeared after sending the first packet or used a forged source address.
A fourth message is unnecessary for the ordinary case. The server's SYN-ACK already confirms the client's SYN, and the client's final ACK confirms the server's SYN. Both initial sequence numbers have been announced and acknowledged after three logical steps.
The handshake is therefore not arbitrary ceremony. It is the smallest ordinary exchange that synchronizes both directions and confirms that the server's reply returned to the initiating endpoint. This synchronization also helps TCP distinguish a new connection attempt from old duplicate connection requests still arriving from the network.
TCP represents connection progress with explicit states.
These states are local. There is no shared connection object stored in the network between the endpoints. Routers forward the packets, but the client and server TCP implementations maintain the connection's authoritative transport state.
The server application does not need to execute code for every handshake packet. The operating-system TCP stack normally processes the SYN, generates the SYN-ACK, validates the final ACK, and places the established connection where the server application can accept it.
An application can therefore be slow to call accept() even though packet capture shows a completed handshake. Conversely, a process can own a listening socket while resource limits or queue pressure prevent new handshakes from completing normally.
Loading simulation...
The endpoints use options in their SYN segments to advertise selected TCP capabilities. "Negotiate" does not always mean that both sides choose one shared number. Several options describe what each endpoint can receive or support in its direction.
Common SYN options include:
Maximum Segment Size (MSS): The largest TCP payload the sender of the option is willing to receive in one segment. Each endpoint can advertise a different value. The path can still require smaller packets.
Window Scale: Enables receive-window values larger than the unscaled TCP header field can represent. Both endpoints must advertise support during the handshake for scaling to be used.
Selective Acknowledgment Permitted: Indicates that the endpoint understands selective acknowledgment information if data later arrives with gaps.
Timestamps: Advertises support for TCP timestamp values used by modern implementations for measurements and segment validation.
A summarized SYN might show:
The options in the client's SYN and server's SYN-ACK can differ because each endpoint advertises its own limits and capabilities.
These options influence later transfer behavior, but they do not negotiate application semantics. The handshake does not select an HTTP version, authenticate a user, choose a database, or create encryption keys.
A successful ordinary handshake provides useful but limited evidence.
It demonstrates that:
It does not prove that:
A TCP handshake and a TLS handshake are different operations. TCP establishes the reliable transport connection. If TLS is used, it performs its security exchange over that transport. Seeing ESTABLISHED at the TCP layer is therefore necessary for traditional TLS-over-TCP traffic, but it is not evidence that TLS or the application request succeeded.
For an ordinary new connection, the client sends SYN and waits for SYN-ACK before it can treat the connection as established and send ordinary application data. That wait is approximately one network round-trip time, or RTT.
On a nearby network, the delay may be small. Across continents or mobile networks, it can be noticeable. Opening several new connections repeatedly pays the setup cost several times.
Reusing an established connection avoids repeating the TCP handshake for every request. This is one reason connection pools and persistent connections matter to backend latency.
The TCP handshake is only one part of request startup. Name resolution, security setup, proxy traversal, and application processing can add their own delays. A timing measurement should separate these phases rather than labeling all startup latency "TCP."
TCP permits a SYN to carry data, but an ordinary receiver does not deliver those bytes to the application until the connection is established. TCP Fast Open supports controlled early use of SYN data to reduce startup latency. Both endpoints and the network path must support that optimization, so the standard operational model remains SYN, SYN-ACK, and ACK before ordinary application exchange.
The three-way handshake describes logical messages, not a guarantee that exactly three packets appear on the wire. Packets can be lost, duplicated, or delayed.
The client remains in SYN-SENT. After waiting, it sends another SYN. Implementations space repeated attempts over time rather than retrying continuously.
If no usable response ever arrives, the connection attempt eventually times out.
The client has no evidence that its SYN arrived, so it can retransmit the SYN. The server can respond with another SYN-ACK for the same pending connection. The server may also retransmit its SYN-ACK while it waits for the final ACK.
Repeated SYN-ACKs in a capture therefore suggest that the server is not receiving a valid final acknowledgment.
The client believes the connection is established after sending the ACK, while the server remains in SYN-RECEIVED.
The server sends the SYN-ACK again. The established client recognizes the repeated SYN-ACK and responds with another ACK. Data sent by the client also carries a valid acknowledgment and can allow the server to complete the pending handshake.
This behavior makes the setup resilient to ordinary packet loss. It also explains why a capture can contain more than three packets without representing multiple application connections.
Handshake failures generally fall into two visible categories.
If the target host receives a SYN for a TCP port with no listening socket, it commonly responds with a reset:
The client can fail quickly with an error such as "connection refused." This response is evidence that the destination, or a device acting for it, actively rejected the attempt.
A service bound only to 127.0.0.1 provides a common example. It can accept local connections while a SYN sent to the machine's external address finds no matching listener and is refused.
If a firewall silently drops the SYN, the SYN-ACK, or the final ACK, one endpoint sees no useful response. It retries until its attempt times out.
Silence can also result from a routing problem, an unavailable host, an overloaded system, or a policy device configured to discard traffic. A timeout alone does not identify which packet or direction failed. Captures at more than one point are often needed to locate the boundary.
The distinction is operationally valuable:
A listening server can have many handshakes in progress while other established connections wait for the application to accept them.
The kernel must track resources such as:
SYN-RECEIVEDExact queue structures and the meaning of the configured backlog vary across operating systems. It is unsafe to assume that one backlog number maps identically to every implementation.
When the relevant capacity is exhausted, new connection attempts can be delayed or dropped even though the process still owns a listening socket. A packet capture may show repeated SYNs, repeated SYN-ACKs, or a handshake that completes but is followed by slow application service.
A SYN flood deliberately sends large numbers of SYNs, often without completing the final ACK, to consume pending-handshake capacity. Operating systems can defend with mechanisms such as SYN cookies, rate controls, and larger or better-managed queues. These defenses help preserve availability; they do not replace application capacity planning.
For a busy backend, handshake health should be observed alongside accept rate, listen-queue pressure, connection limits, CPU load, and application latency.
On Linux or macOS, tcpdump can capture traffic for a server port:
Replace en0 with the relevant interface. The -nn option keeps addresses and ports numeric. The -S option displays absolute TCP sequence numbers.
A simplified capture may look like:
In tcpdump flag notation:
Read the capture in this order:
A host capture can report an outgoing checksum as incorrect because the interface may calculate the final checksum after the capture point. Before diagnosing corruption, account for checksum offloading and confirm the packet at a point after hardware processing.
Several short packet patterns narrow a connection problem quickly.
Repeated SYN with no response: The client is trying, but no usable reply returns. Check the destination address and port, routing, firewall policy, server availability, and whether the capture point is before or after the suspected drop.
SYN followed by RST-ACK: The destination is reachable, but no matching listener accepted the connection or a policy device actively rejected it. Verify the server's bind address and port.
SYN, SYN-ACK, then repeated SYN-ACK: The server's reply reached the capture point, but the final ACK did not return to the server. Investigate the client, the return path, asymmetric routing, and stateful policy devices.
Three-way handshake completes, then the request stalls: Basic TCP establishment succeeded. Look beyond port reachability at security negotiation, application queues, protocol parsing, dependencies, and server processing.
Handshake becomes slow only under load: Inspect listen queues, accept throughput, connection limits, CPU pressure, and upstream devices that track connection state.
Only remote clients fail: Confirm that the process listens on the intended non-loopback address and that host and network firewalls allow the port.
On Linux, socket-state commands can complement a capture:
The first command shows listening TCP sockets. The remaining commands show connections in selected states. The exact state names and filtering syntax differ across operating systems, so use the platform's socket-inspection tool when ss is unavailable.
The client-server exchange is the normal case, but TCP also defines simultaneous open.
In a simultaneous open, both endpoints actively send SYN before receiving the other's SYN. Each endpoint receives a SYN while in SYN-SENT, replies with SYN-ACK, and moves through SYN-RECEIVED toward ESTABLISHED.
The packets can cross in flight, so this is not the ordinary three-packet shape. The same invariant still holds: both initial sequence numbers must be announced and acknowledged before both endpoints are established.
Simultaneous open is rare in conventional client-server applications. Its existence reinforces that TCP defines peer state transitions, while "client" and "server" describe how applications commonly initiate them.
The handshake establishes TCP, not the application session. A completed handshake does not prove that TLS, authentication, HTTP, or database work succeeded.
SYN consumes one sequence number. The first data byte begins at the initial sequence number plus one even when the SYN carried no ordinary payload.
An acknowledgment names the next expected position. Ack = 1001 acknowledges the SYN at sequence number 1000.
The client and server choose independent sequence numbers. The server does not continue counting from the client's ISN.
The kernel normally completes the handshake. The server application does not need to read and answer each SYN itself.
A completed handshake does not mean the server application has called accept(). An established connection can wait in a kernel queue.
A handshake is three logical steps, not always exactly three observed packets. Loss can cause retries, and uncommon variants can produce a different packet shape.
The final ACK can carry data. It does not always need to be an empty packet.
A successful ping does not prove a TCP port is open. ICMP reachability and a listening TCP socket are separate conditions.
A timeout and a refusal mean different things. A reset actively rejects the attempt; silence leaves several possible failure points.
The TCP three-way handshake synchronizes connection state. A listening server performs a passive open; a client actively opens by sending SYN with its initial sequence number and entering SYN-SENT. The server replies with SYN-ACK and enters SYN-RECEIVED; the client's final ACK moves both endpoints to ESTABLISHED.
Each direction has an independent sequence space, and each SYN consumes one position. SYN options advertise MSS, window scaling, selective acknowledgment, and timestamps. Three messages let both initial sequence numbers be announced and acknowledged, costing an ordinary new connection about one round trip before normal application data.
Loss can add retransmissions to a capture. A reset usually means active refusal, while unanswered attempts can indicate loss, filtering, routing failure, overload, or an unavailable endpoint. Completion proves transport establishment, not encryption, application health, or business success.
SYN announces, SYN-ACK announces and confirms, and the final ACK completes confirmation in both directions.
5 quizzes