Sequence Numbers and Acknowledgments
IP delivers packets on a best-effort basis. A packet can be lost, duplicated, delayed, or arrive after packets sent later. TCP sits on top of IP and promises the application something much tidier: a single stream of bytes that arrives complete, in order, and without gaps. To keep that promise, the two ends need a shared way to say which bytes were sent, which ones arrived, and which ones are still missing. Sequence numbers and acknowledgments are that shared language. Without them, a sender cannot tell a delivered segment from a lost one, and a receiver cannot tell a retransmission from new data.
Think of a courier delivering a long letter one page at a time, with the pages arriving out of order. Each page carries a number. The recipient replies with a single line: "I have everything up to page 40, send page 41 next." The sender now knows exactly where things stand without needing a separate receipt for every page. TCP works the same way, except the "pages" are bytes and the numbers are very large.
Why TCP Counts Bytes Instead of Packets
The first design choice is what to number. TCP numbers bytes, not segments. Every byte the sender transmits has its own position in the stream, and that position is its sequence number. A segment is just a container that carries a run of consecutive bytes, so it inherits a sequence number from the first byte it holds.
Numbering bytes gives TCP freedom. The sender can split the stream into segments of whatever size suits the network, and it can later repack or resend those bytes in different segments. A receiver does not care how the bytes were grouped. It cares only about where each byte sits in the stream. If the sender had numbered whole packets instead, a retransmission would have to reproduce the original grouping exactly, and every change to segment size would break the bookkeeping.
A common mistake is to picture the sequence number as a count of segments sent so far. It is not. If the first segment carries 1000 bytes, the next segment's sequence number jumps by 1000, not by 1. The number tracks the amount of data, and that is what makes it useful for confirming receipt.
The Sequence Number Field
Each TCP segment carries a 32-bit field called the sequence number. It holds the position of the first data byte in that segment. The initial value is not zero. Each side picks a random starting number when the connection is set up, which protects against stale segments from an earlier connection being mistaken for current ones.
Once the stream is running, the rule is simple. If a segment starts at sequence number $S$ and carries $L$ bytes of data, the next new byte the sender will transmit is numbered $S + L$. Here is a concrete run. Suppose the starting number is 1000 and the sender transmits three segments of 1000 bytes each:
- The first segment carries bytes 1000 to 1999, so its sequence number is 1000.
- The second carries bytes 2000 to 2999, so its sequence number is 2000.
- The third carries bytes 3000 to 3999, so its sequence number is 3000.
Notice that the numbers advance exactly by the payload length. The receiver can reconstruct the stream's layout just from the sequence numbers and lengths it sees. Because the field is 32 bits, the counter wraps around after $2^{32}$ bytes. That wraparound is a detail the child topic on reliability handles carefully, so for now treat the number as an ever-increasing byte position.
Sequence numbers tell the receiver where data belongs. They do not, by themselves, tell the sender anything. That job falls to the acknowledgment.
Acknowledgment Numbers and Cumulative Confirmation
The acknowledgment number answers one question: what does the receiver still need? It holds the sequence number of the next byte the receiver expects to receive, in order. Every segment sent back to the sender carries this field, and the ACK flag tells the receiver to pay attention to it.
Here is what that means in practice. Continue the example above. The receiver gets the first segment, bytes 1000 to 1999, and has everything from the start of the stream up to byte 1999. It sends back an acknowledgment number of 2000. This says, "Every byte below 2000 is here. Send me 2000 next."
This is called a cumulative acknowledgment, because one number confirms everything that came before it. It is a sentence that says "all bytes up to here arrived intact and in order," not "this particular packet arrived."
That distinction is where most people go wrong. You might assume an ACK means "the packet I sent arrived." It does not. Suppose the second segment arrives but the first was lost. The receiver holds bytes 2000 to 2999, but it cannot advance past the gap at 1000. So it keeps acknowledging 1000, the first byte it is missing. The sender sees an acknowledgment that did not move, and that is the signal that something ahead is missing. The sender does not learn that bytes 2000 to 2999 arrived, at least not from this acknowledgment. The sibling topic on loss recovery explains how the sender responds to that situation.
Quick check: if the receiver's acknowledgment number is 3000, which bytes has it confirmed? Answer: bytes 1000 through 2999, every byte below 3000 that is part of the stream. It has not confirmed byte 3000 itself, because the acknowledgment names the next byte wanted, not the last byte received.
Two Streams, Each With Its Own Numbers
A TCP connection is full duplex. Data flows in both directions at once, and each direction is a separate byte stream with its own sequence numbers. The client's bytes are numbered from the client's starting point, and the server's bytes are numbered from the server's. Neither side's numbers are related to the other's.
This is why acknowledgments travel in both directions. When the server sends data to the client, the client's segments carry acknowledgment numbers for the server's stream. When the client sends a request, the server's replies carry acknowledgments for the client's stream. In a typical request and response exchange, the same segment often does double duty: it carries new data in one direction and an acknowledgment for the other direction. This shortcut is called piggybacking, meaning the acknowledgment rides along on a data segment that was going that way anyway.
Here is the consequence you should keep in mind. Acknowledging the client's bytes says nothing about how the server's bytes are doing. A client can have received every byte the server sent while the server has not yet received a single byte the client wrote. Each direction tracks its own progress.
Set this up as a mental picture before moving on. Two independent counters run in opposite directions, and each segment is a small message that moves one counter forward while reporting the other counter's position.
AI-generated study article from SophistAI. Sign up free to study it with an AI tutor.