
TCP starts a connection
Before TCP sends data, two endpoints exchange three slips and agree their starting numbers. The handshake sets those numbers up. It does not promise that the path stays healthy.
TCP numbers every byte it sends, so before any data goes, each end has to say where its count starts. The exchange that does it is the handshake: three short segments with no data in them.
The two ends have names for their roles. The client is the end that asks to open the connection. The server is the end that waits to be asked. They are roles on one connection, not kinds of machine. A phone, a laptop and an ESP32 can each be either, and one ESP32 can be a client of a web service and a server for your browser at the same time.
The three slips
- The client sends SYN, which carries its own starting number, its initial sequence number.
- The server answers SYN-ACK. It carries the server's own starting number and an acknowledgement of the client's.
- The client sends ACK, acknowledging the server's number.
Each end chooses its own number; the two directions are counted separately. An acknowledgement names the next number expected, so the server's answer to a SYN numbered 4137 says 4138. A SYN uses up one number, and so does a FIN later on, which is why it is one more and not the same. The first byte of data is numbered one past the starting number.
The connection these slips open is named by the four numbers from lesson two: the address and port at each end. A second connection to the same server from the same board uses a different client port, so the two are kept apart.
The numbers are not small
The bench uses numbers you can read. A real stack does not start at 0 or at 1. RFC 9293 describes a number taken from a clock that ticks about every four microseconds, plus a keyed hash of the two addresses and two ports. The clock keeps an old, delayed segment from an earlier connection from being taken for a new one, and the hash stops a stranger from guessing the number.
A lost slip
If a SYN is lost, nothing comes back, and nobody says so. The client waits and sends the same SYN again, and waits longer each time it has to try again. RFC 6298 suggests about one second for the first wait. The Arduino core for the ESP32 builds lwIP with three seconds, so a connect to a host that is not answering takes a while to give up.
A lost SYN-ACK looks the same to the client. A lost final ACK is different: the client believes the connection is open, the server does not yet, and the server sends its SYN-ACK again.
What it proves
When a program's connect call returns, it means these three slips went through, and nothing more. The path can fail a moment later. The server's program may not have started reading. Whatever protocol runs on top, HTTP or MQTT, has not been tried.
Common mistakes
- Starting a count at zero. The first byte after the handshake is the starting number plus one.
- Calling the first end "the server" because it is a board. The client is the one that opens the connection.
- Treating a successful connect as a healthy link. Check the first reply from the application above it.
- Retrying a connect in a tight loop. Each try is a SYN, and the waits exist so that a struggling network is not buried.
Edit this page — content/fundamentals/tcp-udp/tcp-handshake.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.