TCP/IP is the protocol suite that makes the modern internet possible. It’s not one protocol but a stack of them, each responsible for a specific job — moving bits across a wire, addressing machines, ensuring reliable delivery, and letting applications talk to each other. This article breaks the entire suite down from first principles, layer by layer, with practical Linux, Cisco, and Python examples.
The TCP/IP Model
Unlike the 7-layer OSI model used mostly for teaching, TCP/IP in practice uses a simpler 4-layer model:
| TCP/IP Layer | Roughly Maps To OSI | Examples |
|---|---|---|
| Application | Application, Presentation, Session | HTTP, DNS, SSH, SMTP |
| Transport | Transport | TCP, UDP |
| Internet | Network | IP, ICMP, ARP |
| Link (Network Access) | Data Link, Physical | Ethernet, Wi-Fi |
graph TD
App[Application Layer: HTTP, DNS, SSH] --> Trans[Transport Layer: TCP, UDP]
Trans --> Net[Internet Layer: IP, ICMP, ARP]
Net --> Link[Link Layer: Ethernet, Wi-Fi]Data moving down this stack is progressively wrapped — a process called encapsulation. An HTTP request becomes a TCP segment, which becomes an IP packet, which becomes an Ethernet frame, which becomes electrical or optical signals on the wire. At the receiving end, the reverse happens: decapsulation.
Layer by Layer
1. Link Layer
Handles the physical transmission of raw bits and local addressing via MAC addresses. Ethernet (covered in a separate article) is the dominant technology here. This layer doesn’t know anything about IP addresses or the wider internet — only about delivering frames to devices on the same physical segment.
2. Internet Layer
IP (Internet Protocol) is responsible for logical addressing and routing — getting a packet from a source network to a destination network, potentially across many intermediate routers, using IP addresses (see the separate IPv4/IPv6 article for detail).
ICMP (Internet Control Message Protocol) carries diagnostic and error messages — this is what powers ping and traceroute.
ARP (Address Resolution Protocol) maps an IPv4 address to a MAC address on the local segment (IPv6 uses NDP instead, as covered in the IPv4/IPv6 comparison article).
3. Transport Layer
This layer provides end-to-end communication between applications, identified by port numbers.
TCP (Transmission Control Protocol) is connection-oriented and reliable. Before any data is sent, TCP performs a three-way handshake:
Client Server
|------ SYN ------------>|
|<----- SYN-ACK ---------|
|------ ACK ------------>|
|------ Data Transfer -->|
TCP guarantees:
- In-order delivery using sequence numbers
- Reliability via acknowledgments and retransmission
- Flow control via the sliding window mechanism
- Congestion control to avoid overwhelming the network
UDP (User Datagram Protocol) is connectionless and makes no delivery guarantees. It has far less overhead, which makes it ideal for applications like DNS lookups, video streaming, and VoIP, where speed matters more than perfect reliability, and where the application itself can tolerate or handle occasional loss.
| Feature | TCP | UDP |
|---|---|---|
| Connection setup | 3-way handshake | None |
| Reliability | Guaranteed, retransmits lost data | None built-in |
| Ordering | Guaranteed | Not guaranteed |
| Speed | Slower due to overhead | Faster, minimal overhead |
| Header size | 20 bytes minimum | 8 bytes |
| Use cases | Web, email, file transfer, SSH | DNS, streaming, gaming, VoIP |
4. Application Layer
This is where user-facing protocols live: HTTP/HTTPS for the web, DNS for name resolution, SMTP/IMAP/POP3 for email, SSH for secure remote access, and many more (see the companion article on Internet Services and Port Numbers for a full list).
The TCP Three-Way Handshake in Detail
sequenceDiagram
participant C as Client
participant S as Server
C->>S: SYN (seq=100)
S->>C: SYN-ACK (seq=300, ack=101)
C->>S: ACK (ack=301)
Note over C,S: Connection Established
C->>S: Data
S->>C: ACK- SYN: Client sends a segment with the SYN flag set and an initial sequence number.
- SYN-ACK: Server responds with its own SYN, plus an ACK acknowledging the client’s sequence number.
- ACK: Client acknowledges the server’s sequence number. The connection is now established.
Closing a connection uses a similar but distinct four-step process involving FIN and ACK flags, since either side may want to close independently (half-close).
Practical Examples
Inspecting TCP Connections on Linux
# Show established TCP connections with process info
sudo ss -tp
# Capture the handshake live with tcpdump
sudo tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0' -nn
Watching the Handshake with Wireshark Filter
tcp.flags.syn == 1 || tcp.flags.fin == 1
Configuring TCP Keepalive Settings on Linux
# View current TCP keepalive settings
sysctl net.ipv4.tcp_keepalive_time
sysctl net.ipv4.tcp_keepalive_intvl
sysctl net.ipv4.tcp_keepalive_probes
# Tune keepalive to detect dead peers faster
sudo sysctl -w net.ipv4.tcp_keepalive_time=300
Cisco: Viewing IP and Protocol Statistics
Router# show ip traffic
Router# show ip interface brief
Router# show tcp brief all
Python: A Minimal TCP Client and Server
Server:
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(("0.0.0.0", 9999))
server.listen(5)
print("Listening on port 9999...")
while True:
conn, addr = server.accept()
print(f"Connection from {addr}")
data = conn.recv(1024)
print(f"Received: {data.decode()}")
conn.sendall(b"ACK: message received")
conn.close()
Client:
import socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(("127.0.0.1", 9999))
client.sendall(b"Hello, server!")
response = client.recv(1024)
print(f"Server replied: {response.decode()}")
client.close()
This demonstrates TCP’s connection-oriented model directly: connect() performs the three-way handshake behind the scenes before any data is exchanged.
Python: A Minimal UDP Exchange
import socket
# Server
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(("0.0.0.0", 9998))
data, addr = server.recvfrom(1024)
print(f"Received {data} from {addr}")
Notice there’s no listen() or accept() — UDP has no connection state to establish.
Best Practices
- Use TCP for anything requiring guaranteed, ordered delivery (file transfers, database connections, web pages).
- Use UDP for latency-sensitive applications that can tolerate loss (VoIP, live video, DNS).
- Tune TCP window sizes and keepalive settings for high-latency or high-loss links (satellite, mobile networks).
- Always validate MTU settings across a path when troubleshooting fragmentation-related issues.
- Use
ssover the oldernetstaton modern Linux systems — it’s faster and provides more detail. - Capture packets with
tcpdump/Wireshark when application-level troubleshooting isn’t enough.
Troubleshooting
| Symptom | Likely Layer | Diagnostic Command |
|---|---|---|
| No link at all | Link layer | ip link show, check cabling |
| Can ping IP but not resolve names | Application (DNS) | dig, nslookup |
| Connection refused | Transport | ss -tulnp on the server, check firewall |
| Connection times out | Network/Transport | traceroute, check ACLs/firewalls along path |
| Slow transfers despite good ping | Transport (window/MTU) | Check for retransmissions with tcpdump, verify MTU |
Further Reading
Conclusion
TCP/IP’s layered design is what allows the internet to scale — each layer solves one problem and hands off to the next, so a new Link-layer technology (like Wi-Fi 6 or 5G) doesn’t require rewriting how applications work. Understanding how the layers interact, and how TCP and UDP differ in their reliability guarantees, is foundational to nearly every other topic in networking.