TCP Working: 3-Way Handshake & Reliable Communication

Search for a command to run...

No comments yet. Be the first to comment.
You open WhatsApp, type a message, and hit send. A single grey tick appears. You're on a train with no signal, but the message went through anyway. Or did it? That single tick is the beginning of a su

You record a Reel, add a song, trim it, and then close the app before posting. When you reopen Instagram, the draft is right there waiting. Nothing was lost. That experience feels simple. The system b

If you have built a React Native app, you have felt the pain of navigation setup. It is easy to get wrong and hard to keep clean as apps grow. This article explains what routing means, why it matters,

Modern apps are not just a pile of screens. They are a living system of features, data flows, offline behavior, and performance constraints. If you have only shipped small apps, the jump to something

Your application has user accounts. Some routes should only be accessible to logged-in users. How do you protect routes and verify that a request actually comes from who they claim to be? JWT (JSON We

Ashish's Blog
57 posts
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.
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.
TCP addresses several fundamental challenges of network communication:
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.
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.
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).
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.
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).
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).
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."
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
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 |
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.
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.
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.
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.
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
Once the connection is established, data transfer begins. TCP ensures every byte arrives correctly and in order.
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.
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.
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.
TCP's reliability comes from several mechanisms working together.
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) |
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.
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.
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
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
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.
TCP doesn't just ensure reliability—it also prevents overwhelming the receiver or the network.
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.
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.
Just as a connection is established with a handshake, it's closed with a structured process to ensure both sides are done communicating.
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 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.
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
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.
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.
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
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
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
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.