RFC 9293 (the current TCP specification, 2022), cross-checked against Kurose and Ross, sections 3.5.5 and 3.5.6. The flow-control arithmetic was recomputed in code before publishing.
Do flow control first: it is one formula and one inequality. Then draw the handshake and the close as two timelines, writing the flags and numbers on each arrow, and write the state names down the sides. Drawing them twice from memory is enough for most exams.
Some course slides draw the close with the client's final ACK sent from FIN_WAIT_2 and the wait called “timed wait”. The standard name for that state is TIME_WAIT. It lasts twice the maximum segment lifetime, so that a lost final ACK can still be resent. Both descriptions are of the same thing.
The picture: filling a sink#
Picture pouring water into a sink while it drains. If you pour faster than it drains, the sink overflows. You watch the level and slow down when it gets high. That is flow control: the sender watches how much room is left at the receiver and never sends more than fits.
Connection management is simpler still. It is the phone-call ritual: “Hello?”, “Hi, it's me”, “Great, so...” before talking, and “bye”, “bye” at the end.
Vocabulary, before anything uses it#
| Term | What it means |
|---|---|
| Receive buffer | Memory at the receiver where arrived bytes wait until the application reads them. Its size is RcvBuffer. |
| Receive window (rwnd) | How much free space is left in the receive buffer. Sent to the other side in every segment. |
| Flow control | Stopping the sender from overflowing the receiver's buffer. |
| Congestion control | Stopping senders from overloading the network between them. A different problem. |
| SYN | The flag that asks to open a connection and carries the starting sequence number. |
| FIN | The flag that says “I have no more data to send”. |
| ISN | Initial sequence number: the random number each side starts counting its bytes from. |
| Connection state | The stage each side is in, such as LISTEN, ESTABLISHED or TIME_WAIT. |
Flow control and the receive window#
TCP puts arriving bytes into the receive buffer. The application reads from it when it is ready, which might be much later. If the sender keeps sending while the application is busy, the buffer fills and bytes would be lost.
So the receiver keeps track of the room left, using two counters: LastByteRcvd (the last byte put into the buffer) and LastByteRead (the last byte the application has taken out).
rwnd = RcvBuffer − (LastByteRcvd − LastByteRead)
The part in brackets is how much is sitting in the buffer unread. Take that away from the buffer size and you have the free space. The receiver puts rwnd in the receive window field of every segment it sends back.
The sender keeps its own two counters: LastByteSent and LastByteAcked. The difference is how much data is “in flight”: sent but not yet acknowledged. The rule is:
LastByteSent − LastByteAcked ≤ rwnd
The receive buffer is 65,536 bytes. The receiver has received up to byte 50,000 and the application has read up to byte 30,000. So 20,000 bytes are waiting, and rwnd = 65,536 − 20,000 = 45,536 bytes. The sender may have at most 45,536 unacknowledged bytes in flight.
The zero-window trap
What if the buffer fills and the receiver advertises rwnd = 0? The sender stops. But the receiver only sends a segment when it has data or an ACK to send, so it might never tell the sender that space has opened up. To avoid being stuck forever, TCP makes the sender keep sending tiny one-byte segments while rwnd is 0. Each one is acknowledged, and eventually an ACK carries a non-zero rwnd.
Flow control protects the receiver. Congestion control protects the network. Both slow the sender down, but for different reasons. rwnd is the flow-control tool.
Opening a connection: the three-way handshake#
Before any data flows, both sides must agree to talk and each must learn the other's starting sequence number. It takes three segments:
| Segment | Flags and numbers | Meaning | State after |
|---|---|---|---|
| 1. Client → server | SYN = 1, Seq = x | “I want to connect. My bytes will start at x.” No data. | Client: SYN_SENT |
| 2. Server → client | SYN = 1, Seq = y, ACK = 1, ACKnum = x + 1 | “OK. My bytes start at y, and I expect your byte x + 1 next.” This is the SYNACK. | Server: SYN_RCVD. The client becomes ESTABLISHED when this arrives. |
| 3. Client → server | ACK = 1, ACKnum = y + 1 | “Got it, I expect your byte y + 1.” This one may carry data. | Server: ESTABLISHED |
The server starts in the LISTEN state, waiting for SYNs. x and y are chosen randomly by each side: the ISNs.
The client picks ISN 4,000 and the server picks ISN 9,000. The segments are: SYN with Seq 4,000; SYNACK with Seq 9,000 and ACKnum 4,001; ACK with ACKnum 9,001. The SYN “uses up” one sequence number even though it carries no data, which is why the ACK is x + 1.
Why not two segments? On a real network, old SYN segments can be delayed and arrive late, or be retransmitted. With a two-way handshake, a server could open a connection nobody wants: a half-open connection. The third segment confirms the client really is there and really means this connection.
A server sets aside memory for each connection when it receives a SYN. An attacker can send a flood of SYNs with fake source addresses and never finish the handshake, filling the server's memory with half-open connections. This denial-of-service attack is a SYN flood. The standard defense, SYN cookies, lets the server avoid storing anything until the final ACK proves the client is real.
Closing a connection: FIN and ACK, both ways#
A TCP connection carries data in both directions, and each direction is closed on its own. Either side may start. Here the client closes first:
| Segment | Flags and numbers | Meaning | State after |
|---|---|---|---|
| 1. Client → server | FIN = 1, Seq = x | “I have no more data.” | Client: FIN_WAIT_1 |
| 2. Server → client | ACK = 1, ACKnum = x + 1 | “Noted.” The server may still send data. | Server: CLOSE_WAIT. Client: FIN_WAIT_2 |
| 3. Server → client | FIN = 1, Seq = y | “I am done too.” | Server: LAST_ACK |
| 4. Client → server | ACK = 1, ACKnum = y + 1 | “Noted.” | Client: TIME_WAIT. Server: CLOSED |
The client does not close immediately. It waits in TIME_WAIT for twice the maximum segment lifetime. If its last ACK was lost, the server will resend its FIN, and the client is still around to ACK it again. Then the client moves to CLOSED.
Open with three: SYN, SYNACK, ACK. Close with four: FIN, ACK, FIN, ACK. The side that closes first is the side that waits in TIME_WAIT.
FIN means “I will send no more”, not “I will receive no more”. Between steps 2 and 3 the connection is half-closed: the server can still send data to the client.
RST is not a normal close. It aborts a connection at once, for example when a segment arrives for a port with no listening socket.
The receiver advertises rwnd, its free buffer space, and the sender keeps its unacknowledged bytes within it; a connection opens with SYN, SYNACK, ACK and closes each direction with its own FIN and ACK.
Practice#
For the handshake and the close, draw the timeline and label the state on each side after every arrow.
- What problem does flow control solve, and how is it different from congestion control?
- RcvBuffer is 32,768 bytes. LastByteRcvd is 40,000 and LastByteRead is 20,000. What is rwnd?
- The receiver in question 2 advertises that rwnd. The sender's LastByteSent is 80,000 and LastByteAcked is 75,000. How many more bytes may it send right now?
- What does a sender do when rwnd is 0, and why?
- The client's ISN is 1,200 and the server's is 5,600. Write the three handshake segments with their flags, sequence numbers and ACK numbers.
- Which handshake segment may carry application data?
- Why does TCP use three segments to open a connection instead of two?
- List the four segments of a normal close started by the client, and the state the client is in after each of its steps.
- Why does the side that closes first wait in TIME_WAIT instead of closing immediately?
Solutions#
Find the first line where your working split from the solution, and redo the problem from that line. Do not just read the answer and nod.
- It stops the sender overflowing the receiver's buffer. Congestion control instead stops senders overloading the network between them. Flow control is about the receiver; congestion control is about the network.
- 12,768 bytes. Unread data: 40,000 − 20,000 = 20,000. rwnd = 32,768 − 20,000 = 12,768.
- 7,768 bytes. In flight: 80,000 − 75,000 = 5,000. The limit is 12,768, so it may send 12,768 − 5,000 = 7,768 more.
- It keeps sending one-byte segments. The receiver might otherwise never send a segment announcing that space has opened. Each one-byte segment is acknowledged, so an update with a non-zero rwnd will eventually reach the sender.
- SYN, SYNACK, ACK. (1) SYN = 1, Seq = 1,200. (2) SYN = 1, ACK = 1, Seq = 5,600, ACKnum = 1,201. (3) ACK = 1, ACKnum = 5,601 (its Seq is 1,201).
- The third one, the final ACK. The SYN and SYNACK carry no application data.
- To avoid half-open connections from old or duplicate SYNs. A delayed or retransmitted SYN could otherwise make the server open a connection the client does not want. The third segment confirms the client is really there for this connection.
- FIN, ACK, FIN, ACK. Client sends FIN (client in FIN_WAIT_1). Server ACKs (client in FIN_WAIT_2). Server sends FIN. Client ACKs (client in TIME_WAIT, then CLOSED).
- So it can re-ACK a resent FIN. If its final ACK was lost, the other side will retransmit its FIN. Waiting twice the maximum segment lifetime means the client is still there to answer, and old segments from this connection have died out before the port can be reused.
Test-readiness checklist#
- ☐ I can state what flow control prevents and how it differs from congestion control.
- ☐ I can compute rwnd from RcvBuffer, LastByteRcvd and LastByteRead.
- ☐ I can apply LastByteSent − LastByteAcked ≤ rwnd.
- ☐ I can explain the zero-window problem and the one-byte fix.
- ☐ I can write all three handshake segments with flags, Seq and ACK numbers from two ISNs.
- ☐ I can explain why the handshake needs three segments, and what a SYN flood exploits.
- ☐ I can write the four closing segments and name the states, including FIN_WAIT_1, FIN_WAIT_2, CLOSE_WAIT, LAST_ACK and TIME_WAIT.
Every SYN and every FIN uses up one sequence number, even with no data. So the reply always acknowledges x + 1. Forgetting the + 1 is the most common error on handshake questions.
Frequently asked questions#
How is the TCP receive window calculated?
rwnd = RcvBuffer minus (LastByteRcvd minus LastByteRead). In words: the buffer size minus the bytes that have arrived but not yet been read by the application. The sender keeps LastByteSent minus LastByteAcked at or below rwnd.
What is the difference between flow control and congestion control?
Flow control stops a sender from overflowing the receiver's buffer and uses the receive window. Congestion control stops senders from overloading the network between them. Both slow the sender, for different reasons.
What are the steps of the TCP three-way handshake?
The client sends a SYN with its initial sequence number x. The server replies with a SYNACK carrying its own initial sequence number y and acknowledgment x + 1. The client sends an ACK with acknowledgment y + 1, which may also carry data.
How is a TCP connection closed?
Each direction is closed separately. One side sends a FIN, the other acknowledges it, later sends its own FIN, and the first side acknowledges that. The side that closed first then waits in TIME_WAIT before fully closing.
What is a SYN flood attack?
A denial-of-service attack in which an attacker sends many SYN segments, usually with fake source addresses, and never completes the handshake. The server's resources fill with half-open connections. SYN cookies defend against it by not storing state until the handshake completes.
Something here not clear? A topic you wish we covered? Tell us. We read every message, and a request is the fastest way to get a guide written.
Test yourself on this topic
The free networking practice app has 19 questions on this topic and 500 across the whole course, each with an explanation for every answer. Offline, no ads, no account.
Practise this topic →