TCP Working: 3-Way Handshake & Reliable Communication

What Happens When Data is Sent Without Rules?
Imagine you're trying to have a conversation with someone across a noisy, crowded room. You shout your message, but:
They might not hear you (data loss)
They might hear only part of what you said (incomplete data)
They might hear your sentences out of order (data arrives scrambled)
You have no idea if they understood you (no confirmation)
Someone else's conversation might interfere with yours (collision)
This chaos is essentially what happens when you send data over a network without any rules. The internet is like that crowded room—packets can get lost, arrive late, arrive out of order, or get corrupted along the way.
The fundamental problem: The internet is inherently unreliable. Networks are messy, cables fail, routers get overloaded, and packets take unpredictable paths. If you just throw data at the network and hope for the best, you'll get unpredictable, unreliable results.
That's where TCP comes in.
What is TCP and Why is it Needed?
TCP (Transmission Control Protocol) is a set of rules that ensures reliable, ordered, error-free communication between two computers over an unreliable network.
Think of TCP as a highly organized conversation protocol:
Without TCP (chaos):
You: "Can you..."
You: "...send me..."
You: "...the report?"
They hear: "...the report?" "Can you..." (out of order, missing words)
With TCP (organized):
You: "Can I start talking?" (establish connection)
They: "Yes, I'm listening" (confirm connection)
You: "Great, here's my message piece 1 of 3"
They: "Got piece 1, send the next"
You: "Here's piece 2 of 3"
They: "Got piece 2, send the next"
You: "Here's piece 3 of 3"
They: "Got piece 3, message complete"
TCP transforms the unreliable, chaotic internet into a reliable, ordered communication channel.
Problems TCP is Designed to Solve
TCP addresses several fundamental challenges of network communication:
Problem 1: Packets Get Lost
Networks drop packets for various reasons—congested routers, failed links, full buffers. Without TCP, lost packets are just gone.
TCP's solution: Detect lost packets and retransmit them automatically.
Problem 2: Packets Arrive Out of Order
The internet routes packets independently. Packet A might take one path (fast), Packet B might take another (slow), so B arrives before A.
TCP's solution: Use sequence numbers to reassemble packets in the correct order.
Problem 3: Packets Get Corrupted
Electrical interference, hardware failures, or bit flips can corrupt data during transmission.
TCP's solution: Use checksums to detect corruption and discard bad packets (which triggers retransmission).
Problem 4: No Confirmation of Receipt
When you send data, you don't know if it arrived. Did the recipient get it? Did they process it? Are they still there?
TCP's solution: Every received packet is acknowledged. If an acknowledgment doesn't arrive, retransmit the packet.
Problem 5: Network Congestion
If you send data too fast, you can overwhelm the network or the receiver.
TCP's solution: Flow control (receiver tells sender how much buffer space they have) and congestion control (sender adjusts speed based on network conditions).
Problem 6: No Connection State
Without a formal connection, there's no way to know if both sides are ready to communicate.
TCP's solution: Establish a connection before data transfer begins, ensuring both sides are ready.
The Big Picture:
TCP takes an unreliable network (where packets can be lost, delayed, duplicated, corrupted, or reordered) and creates a reliable communication channel (where data arrives complete, correct, and in order).
The TCP 3-Way Handshake: Establishing a Connection
Before any data can be transferred, TCP establishes a connection using a process called the 3-way handshake. This is TCP's way of saying, "Are you ready? I'm ready. Let's do this."
Why Do We Need a Handshake?
Imagine calling someone on the phone:
Without a handshake:
You: "Here's the information you need..."
(No one answered. Your message was wasted.)
With a handshake:
You: "Hello?" (SYN)
Them: "Hello! I can hear you." (SYN-ACK)
You: "Great! Here's what I wanted to say..." (ACK + data)
The handshake ensures:
The receiver is listening and ready
Both sides agree on initial settings (sequence numbers)
Both sides allocate resources for the connection
The Three Steps of the Handshake
Let's break down the TCP 3-way handshake step by step.
Client Server
| |
| 1. SYN |
|------------------------------------>|
| "Can I connect?" |
| |
| 2. SYN-ACK |
|<------------------------------------|
| "Yes! I'm ready too." |
| |
| 3. ACK |
|------------------------------------>|
| "Great, let's communicate!" |
| |
|===== CONNECTION ESTABLISHED ========|
| |
| Data transfer begins |
Step 1: SYN (Synchronize)
The client initiates the connection.
Client → Server: SYN packet
What the SYN packet contains:
SYN flag set: This is a synchronization request
Initial sequence number (ISN): A random number (let's say 1000)
Window size: How much data the client can receive
Options: TCP options like maximum segment size
In simple terms: "Hi Server! I'm Client, and I want to talk to you. My starting sequence number is 1000. I can receive up to 64KB at a time."
What the client is doing:
Asking permission to connect
Proposing an initial sequence number for tracking packets
Informing the server of its capabilities
The client now waits for a response.
Step 2: SYN-ACK (Synchronize-Acknowledge)
The server responds, agreeing to the connection.
Server → Client: SYN-ACK packet
What the SYN-ACK packet contains:
SYN flag set: Server also wants to synchronize
ACK flag set: Server acknowledges client's SYN
Acknowledgment number: Client's ISN + 1 (1000 + 1 = 1001)
Server's initial sequence number: A random number (let's say 5000)
Window size: How much data the server can receive
In simple terms: "Hi Client! I received your request. My starting sequence number is 5000. I acknowledge that your sequence number is 1000, and I'm ready to receive data starting at sequence number 1001. I can receive up to 128KB at a time."
What the server is doing:
Confirming it received the client's SYN (by acknowledging 1001)
Sending its own SYN with its starting sequence number
Allocating resources for this connection
The server now waits for the client's final acknowledgment.
Step 3: ACK (Acknowledge)
The client confirms the server's response.
Client → Server: ACK packet
What the ACK packet contains:
ACK flag set: Acknowledging the server's SYN
Acknowledgment number: Server's ISN + 1 (5000 + 1 = 5001)
Sequence number: Client's next sequence (1001)
In simple terms: "Server! I received your SYN. I acknowledge your sequence number 5000, and I'm ready to receive data starting at sequence number 5001. Let's start communicating!"
What the client is doing:
Confirming it received the server's SYN-ACK
Finalizing the connection setup
Connection established! Both sides can now send data.
Why THREE Steps? Can't We Do It in Two?
You might wonder: why not just SYN and SYN-ACK? Why the third ACK?
The problem with 2-way handshake:
Imagine the server's SYN-ACK gets delayed but not lost:
Client ----SYN----> Server
Client <--SYN-ACK-- Server (delayed, arrives late)
Client thinks: "No response, connection failed"
Server thinks: "Connection established, I'm waiting for data"
The server would allocate resources for a connection that the client abandoned. An attacker could exploit this to exhaust server resources (SYN flood attack).
The 3-way handshake prevents this:
The server only fully commits resources AFTER receiving the final ACK. If the ACK doesn't arrive, the server knows the connection failed and can clean up.
The Handshake in Real Life: Detailed Example
Let's see the handshake with actual numbers:
CLIENT SERVER
| |
| SYN |
| Seq = 1000 |
| ----------------------------------> |
| |
| "I want to connect" |
| "My sequence starts at 1000" |
| |
| | (Server receives SYN)
| | (Allocates resources)
| |
| SYN-ACK |
| Seq = 5000 |
| Ack = 1001 |
| <-------------------------------- |
| |
| (Client receives SYN-ACK) |
| "Server is ready" |
| "Server's sequence starts at 5000" |
| "Server expects my next byte is 1001" |
| |
| ACK |
| Seq = 1001 |
| Ack = 5001 |
| ----------------------------------> |
| |
| | (Server receives ACK)
| | (Connection fully established)
| |
|========= CONNECTED ==================|
| |
| Can now send application data |
Key observations:
Sequence numbers start at random values (not 0 or 1) for security reasons
Acknowledgment number = received sequence + 1 (the next expected byte)
Both sides propose their sequence numbers and acknowledge each other's
After the handshake, both sides know:
The other side is alive and ready
Where to start tracking data (sequence numbers)
Buffer sizes for flow control
How Data Transfer Works in TCP
Once the connection is established, data transfer begins. TCP ensures every byte arrives correctly and in order.
Sequence Numbers: Tracking Every Byte
TCP assigns a sequence number to every byte of data. This allows:
Detection of missing data
Reassembly of out-of-order packets
Prevention of duplicate data
Example data transfer:
Client Server
| |
| Data: "Hello" (5 bytes) |
| Seq = 1001, Len = 5 |
| ----------------------------------> |
| |
| ACK |
| Ack = 1006 |
| <-------------------------------- |
| |
| Data: "World" (5 bytes) |
| Seq = 1006, Len = 5 |
| ----------------------------------> |
| |
| ACK |
| Ack = 1011 |
| <-------------------------------- |
Breaking it down:
Client sends "Hello" (5 bytes)
Sequence number: 1001 (where this data starts)
Length: 5 bytes
These bytes occupy positions 1001-1005
Server acknowledges with Ack = 1006
"I received bytes 1001-1005"
"I'm ready for byte 1006 next"
Client sends "World" (5 bytes)
Sequence number: 1006 (next byte after "Hello")
Length: 5 bytes
These bytes occupy positions 1006-1010
Server acknowledges with Ack = 1011
"I received bytes 1006-1010"
"I'm ready for byte 1011 next"
The sequence number increments by the number of bytes sent. This is how TCP tracks every single byte.
Bidirectional Communication
TCP is full-duplex, meaning both sides can send data simultaneously.
Client Server
| |
| Data: "Request" (Seq=1001) |
| ----------------------------------> |
| |
| Data: "Response" |
| (Seq=5001) |
| <-------------------------------- |
| |
| ACK (Ack=5008) |
| ----------------------------------> |
| |
| ACK (Ack=1008) |
| <-------------------------------- |
Each direction has its own sequence numbers and acknowledgments. The client's outgoing data uses one sequence, and the server's outgoing data uses another.
Cumulative Acknowledgments
TCP acknowledgments are cumulative, meaning an ACK acknowledges all data up to that point.
Example:
Client sends:
- Packet 1: Seq=1000, Len=100 (bytes 1000-1099)
- Packet 2: Seq=1100, Len=100 (bytes 1100-1199)
- Packet 3: Seq=1200, Len=100 (bytes 1200-1299)
Server receives all three:
- Sends ACK=1300 (acknowledges all bytes from 1000-1299)
The single ACK=1300 confirms all three packets were received. The client doesn't need separate ACKs for each packet.
How TCP Ensures Reliability
TCP's reliability comes from several mechanisms working together.
1. Acknowledgments
Every packet must be acknowledged. If a packet isn't acknowledged within a timeout period, it's retransmitted.
Client Server
| |
| Data: Seq=1000, Len=100 |
| ----------------------------------> |
| | ✓ Received
| ACK=1100 |
| <-------------------------------- |
| |
| (Client knows packet was received) |
2. Retransmission on Packet Loss
If an ACK doesn't arrive, the sender retransmits the packet.
Client Server
| |
| Data: Seq=1000, Len=100 |
| ---------------------X | ✗ Lost
| |
| (Wait for ACK...) |
| (Timeout - no ACK received) |
| |
| Data: Seq=1000, Len=100 (RETRANSMIT) |
| ----------------------------------> |
| | ✓ Received
| ACK=1100 |
| <-------------------------------- |
How the timeout works:
TCP maintains a Retransmission Timeout (RTO) based on measured round-trip time (RTT). If an ACK doesn't arrive within RTO, the packet is retransmitted.
3. Handling Out-of-Order Packets
Packets might arrive out of order. TCP uses sequence numbers to reassemble them correctly.
Client sends in order:
Packet 1: Seq=1000, Len=100
Packet 2: Seq=1100, Len=100
Packet 3: Seq=1200, Len=100
Network delivers out of order:
Server receives: Packet 1 (Seq=1000) ✓
Server receives: Packet 3 (Seq=1200) ✓
Server receives: Packet 2 (Seq=1100) ✓
TCP reassembles:
Bytes 1000-1099 (Packet 1)
Bytes 1100-1199 (Packet 2)
Bytes 1200-1299 (Packet 3)
Application receives: Data in correct order
The TCP layer buffers out-of-order packets and reassembles them before passing data to the application.
4. Duplicate Detection
Sometimes the network duplicates packets. TCP uses sequence numbers to detect and discard duplicates.
Client sends: Seq=1000, Len=100
Network duplicates packet:
Server receives: Seq=1000, Len=100 (first copy)
Server receives: Seq=1000, Len=100 (duplicate)
TCP detects: "Already received bytes 1000-1099"
TCP action: Discard duplicate, send ACK anyway
5. Checksums for Corruption Detection
Every TCP packet includes a checksum. If the checksum doesn't match, the packet is corrupted and is discarded (which triggers retransmission).
Client sends: Data + Checksum
Network corrupts data:
Server receives: Corrupted data + Checksum
Server calculates checksum: Doesn't match!
Server action: Discard packet silently
Client: (No ACK received, timeout)
Client: Retransmit packet
Putting It All Together: Reliable Transfer
Let's see all these mechanisms working together:
Client Server
| |
| Packet 1: Seq=1000, Len=100 |
| ----------------------------------> | ✓ Received
| ACK=1100 |
| <-------------------------------- |
| |
| Packet 2: Seq=1100, Len=100 |
| ---------------------X | ✗ Lost
| |
| Packet 3: Seq=1200, Len=100 |
| ----------------------------------> | ✓ Received (out of order)
| ACK=1100 (duplicate) |
| <-------------------------------- |
| "Still waiting for 1100-1199" |
| |
| (Timeout for Packet 2) |
| |
| Packet 2: Seq=1100, Len=100 (RETRANS) |
| ----------------------------------> | ✓ Received
| |
| (Server now has 1000-1299) |
| (Reassembles and delivers in order) |
| |
| ACK=1300 |
| <-------------------------------- |
What happened:
Packet 1 arrived successfully
Packet 2 was lost
Packet 3 arrived but is out of order (server still waiting for 1100-1199)
Server sends duplicate ACK=1100 ("I need 1100 next")
Client timeout expires, retransmits Packet 2
Server receives Packet 2, now has all data
Server reassembles 1000-1299 in order
Server sends cumulative ACK=1300
From the application's perspective: Data arrived perfectly, in order, with no errors. All the complexity of loss, retransmission, and reordering was handled transparently by TCP.
Flow Control and Congestion Control
TCP doesn't just ensure reliability—it also prevents overwhelming the receiver or the network.
Flow Control: Respecting the Receiver
The receiver might not be able to process data as fast as the sender can send it. Flow control prevents buffer overflow.
How it works:
Every ACK includes a window size field that tells the sender how much buffer space the receiver has.
Client Server
| |
| ACK=1100, Win=2000 |
| <-------------------------------- |
| "I can receive 2000 more bytes" |
| |
| Sends 2000 bytes |
| ----------------------------------> |
| |
| ACK=3100, Win=0 |
| <-------------------------------- |
| "My buffer is full, stop sending!" |
| |
| (Client stops sending) |
| (Waits for window to open) |
| |
| ACK=3100, Win=1000 |
| <-------------------------------- |
| "I processed some data, you can |
| send 1000 bytes now" |
| |
| Sends 1000 bytes |
| ----------------------------------> |
This prevents the sender from overwhelming the receiver's buffers.
Congestion Control: Respecting the Network
The network itself can get congested. TCP adjusts its sending rate based on network conditions.
Key mechanisms:
Slow start: Start sending slowly, gradually increase speed
Congestion avoidance: Once at a good speed, increase cautiously
Fast retransmit: If duplicate ACKs indicate loss, retransmit immediately
Fast recovery: After packet loss, reduce speed and recover gradually
Simplified view:
Sending rate over time:
Rate
| Slow Start
| /|
| / |
| / | Congestion Avoidance
| / | /
| / | /
| / | /
| / |/
| / |\ Packet Loss!
|/ | \ (Reduce rate)
| | \___________
+--------------------------> Time
TCP constantly adapts to network conditions, sending faster when possible and slowing down when congestion is detected.
How a TCP Connection is Closed
Just as a connection is established with a handshake, it's closed with a structured process to ensure both sides are done communicating.
The 4-Way Connection Termination
Closing a TCP connection requires four steps (sometimes called the "4-way handshake"):
Client Server
| |
| 1. FIN |
| ----------------------------------> |
| "I'm done sending data" |
| |
| 2. ACK |
| <-------------------------------- |
| "OK, I acknowledge that" |
| |
| 3. FIN |
| <-------------------------------- |
| "I'm also done sending data" |
| |
| 4. ACK |
| ----------------------------------> |
| "OK, connection closed" |
| |
|===== CONNECTION CLOSED ==============|
Step-by-Step Connection Termination
Step 1: Client sends FIN
Client → Server: FIN packet
"I have no more data to send. I'm ready to close my side of the connection."
The client can still receive data from the server (this is a half-close).
Step 2: Server sends ACK
Server → Client: ACK packet
"I acknowledge your FIN. I know you're done sending."
The server might still have data to send, so it doesn't close yet.
Step 3: Server sends FIN
Server → Client: FIN packet
"I've finished sending all my data too. I'm ready to close."
Now the server is also done.
Step 4: Client sends ACK
Client → Server: ACK packet
"I acknowledge your FIN. Connection is fully closed."
Both sides have finished, and the connection is terminated.
Why 4 Steps Instead of 3?
Because TCP is full-duplex (data can flow in both directions independently), each direction must be closed separately.
The timeline:
1. FIN from Client: Client → Server direction closed
2. ACK from Server: Server acknowledges
3. FIN from Server: Server → Client direction closed
4. ACK from Client: Client acknowledges
Both directions closed = Connection fully terminated
Optimized Close: FIN+ACK
Sometimes steps 2 and 3 can be combined:
Client Server
| |
| FIN |
| ----------------------------------> |
| |
| FIN+ACK |
| <-------------------------------- |
| (Server ACKs client FIN and sends |
| its own FIN in same packet) |
| |
| ACK |
| ----------------------------------> |
| |
|===== CONNECTION CLOSED ==============|
This reduces the close from 4 packets to 3.
TIME_WAIT State
After sending the final ACK, the client enters a TIME_WAIT state for about 30-120 seconds.
Why?
If the final ACK gets lost, the server will retransmit its FIN. The client needs to stay alive long enough to re-ACK if necessary.
Client Server
| |
| ACK (final) |
| ---------------------X | (Lost)
| |
| (Enters TIME_WAIT) |
| |
| FIN (retransmit) |
| <-------------------------------- |
| |
| ACK (re-send) |
| ----------------------------------> |
| |
| (Wait 30-120 seconds) |
| (Finally close) |
After TIME_WAIT expires, the connection is fully cleaned up.
The Complete TCP Connection Lifecycle
Let's see the entire lifecycle from start to finish:
CLIENT SERVER
========== CONNECTION ESTABLISHMENT ==========
SYN (Seq=1000)
---------------------------------->
(Listen)
SYN-ACK (Seq=5000, Ack=1001)
<----------------------------------
ACK (Ack=5001)
---------------------------------->
(Connected)
========== DATA TRANSFER ==========
Data: "GET /index.html" (Seq=1001)
---------------------------------->
ACK (Ack=1017)
<----------------------------------
Data: "HTTP/1.1 200 OK..." (Seq=5001)
<----------------------------------
ACK (Ack=5500)
---------------------------------->
========== CONNECTION TERMINATION ==========
FIN (Seq=1017)
---------------------------------->
ACK (Ack=1018)
<----------------------------------
FIN (Seq=5500)
<----------------------------------
ACK (Ack=5501)
---------------------------------->
(TIME_WAIT for 30-120 seconds)
========== CONNECTION CLOSED ==========
The journey:
Handshake (3 packets): Establish connection, agree on sequence numbers
Data transfer (variable): Exchange application data with ACKs
Termination (4 packets): Gracefully close both directions
TIME_WAIT: Ensure clean shutdown
Closed: Resources deallocated
Key Takeaways
TCP ensures reliable communication through:
3-way handshake: Establishes connection before data transfer
SYN: Request connection
SYN-ACK: Accept connection
ACK: Confirm connection
Sequence numbers: Track every byte of data
Detect missing data
Reassemble out-of-order packets
Prevent duplicates
Acknowledgments: Confirm receipt of data
Cumulative ACKs
Trigger retransmission if missing
Retransmission: Resend lost packets
Timeout-based
Fast retransmit on duplicate ACKs
Checksums: Detect corruption
Discard corrupted packets
Trigger retransmission
Flow control: Prevent overwhelming receiver
Window size management
Backpressure mechanism
Congestion control: Prevent overwhelming network
Slow start, congestion avoidance
Adapt to network conditions
4-way termination: Gracefully close connection
FIN from both sides
ACKs for both FINs
TIME_WAIT for cleanup
Why This Matters for Developers
Understanding TCP helps you:
1. Debug network issues:
"Connection refused" = No one listening (no SYN-ACK)
"Connection timeout" = SYN sent but no response
"Connection reset" = RST packet received (abrupt close)
2. Optimize performance:
Reduce round trips (handshake overhead)
Use connection pooling (reuse connections)
Understand keep-alive (maintain connections)
3. Design better systems:
Know when to use persistent connections (HTTP/1.1)
Understand WebSocket upgrades (from HTTP to raw TCP)
Appreciate HTTP/3's switch from TCP to QUIC
4. Handle failures gracefully:
Implement proper timeout handling
Design for retries and idempotency
Handle half-open connections
5. Monitor systems:
Watch connection states (ESTABLISHED, TIME_WAIT, CLOSE_WAIT)
Track retransmission rates
Monitor connection pool exhaustion
Final Thoughts
TCP is one of the most successful protocols ever designed. It takes the chaotic, unreliable internet and makes it behave like a reliable, ordered communication channel.
Every time you:
Load a webpage (HTTP over TCP)
Send an email (SMTP over TCP)
Download a file (FTP/SFTP over TCP)
Connect to a database (MySQL/PostgreSQL over TCP)
Use SSH to access a server (SSH over TCP)
...TCP is working behind the scenes, ensuring your data arrives complete, correct, and in order.
The 3-way handshake establishes the connection. Sequence numbers and acknowledgments track every byte. Retransmissions handle packet loss. Checksums detect corruption. Flow control and congestion control optimize performance. And the 4-way termination gracefully closes the connection.
It's a beautiful dance of protocols, all working together to make reliable communication possible over an inherently unreliable network. Understanding how TCP works makes you a better engineer—whether you're building web applications, debugging network issues, or designing distributed systems.
So the next time you type a URL and a webpage appears, remember: TCP just executed a handshake, transferred thousands of packets with sequence numbers and ACKs, handled packet loss and reordering, and will eventually close the connection gracefully—all in a fraction of a second, all to ensure your browsing experience is reliable and seamless.




