Topic Overview
TCP vs UDP
Compare TCP and UDP protocols, their characteristics, and when to use each.
TCP (Transmission Control Protocol) and UDP (User Datagram Protocol) are the two main transport layer protocols, each with different characteristics and use cases.
TCP (Transmission Control Protocol)
Characteristics:
- Connection-oriented: Establishes connection before data transfer
- Reliable: Guarantees delivery, ordered delivery
- Flow control: Adjusts sending rate based on receiver capacity
- Congestion control: Adapts to network conditions
- Full-duplex: Bidirectional communication
Header size: 20-60 bytes
Use cases: Web browsing (HTTP), email (SMTP), file transfer (FTP), database connections
UDP (User Datagram Protocol)
Characteristics:
- Connectionless: No connection establishment
- Unreliable: No delivery guarantee, no ordering
- No flow control: Sends at maximum rate
- No congestion control: Doesn't adapt to network
- Lightweight: Minimal overhead
Header size: 8 bytes
Use cases: DNS queries, video streaming, online gaming, VoIP, DHCP
Comparison
| Feature | TCP | UDP |
|---|---|---|
| Connection | Connection-oriented | Connectionless |
| Reliability | Reliable delivery | Best effort |
| Ordering | Ordered delivery | No ordering |
| Flow Control | Yes | No |
| Congestion Control | Yes | No |
| Overhead | High (20-60 bytes) | Low (8 bytes) |
| Speed | Slower | Faster |
| Use Case | Data integrity critical | Speed critical |
Examples
TCP Example: HTTP Request
// TCP connection establishment
const socket = new TCPSocket();
await socket.connect('example.com', 80); // 3-way handshake
// Send HTTP request
await socket.write('GET / HTTP/1.1\r\nHost: example.com\r\n\r\n');
// Receive response (guaranteed delivery)
const response = await socket.read();
UDP Example: DNS Query
// UDP - no connection
const socket = new UDPSocket();
// Send DNS query
const query = buildDNSQuery('example.com');
await socket.send(query, '8.8.8.8', 53);
// Receive response (may be lost, no guarantee)
try {
const response = await socket.receive(1000); // 1 second timeout
} catch (timeout) {
// Retry or use fallback
}
Common Pitfalls
- Using TCP for everything: Overhead unnecessary for some use cases. Fix: Use UDP when speed > reliability
- Using UDP for critical data: Data loss unacceptable. Fix: Use TCP or add reliability on top of UDP
- Not handling UDP packet loss: Assuming delivery. Fix: Implement retry/timeout logic
- Ignoring TCP overhead: High latency for real-time apps. Fix: Consider UDP with custom reliability
Interview Questions
Beginner
Q: What are the main differences between TCP and UDP?
A:
TCP:
- Connection-oriented, reliable, ordered delivery
- Higher overhead, slower
- Use when data integrity is critical
UDP:
- Connectionless, unreliable, no ordering
- Lower overhead, faster
- Use when speed is critical and some loss is acceptable
Example: HTTP uses TCP (need reliable delivery). DNS uses UDP (speed matters, can retry).
Intermediate
Q: When would you choose UDP over TCP? Give examples.
A:
Choose UDP when:
- Real-time applications: Video streaming, gaming (latency critical)
- Simple request-response: DNS queries (can retry if lost)
- Broadcast/multicast: One-to-many communication
- High throughput: When you need maximum speed
Examples:
- DNS: UDP for queries (fast, can retry)
- Video streaming: UDP for live video (some packet loss acceptable)
- Online gaming: UDP for game state (old updates irrelevant)
- VoIP: UDP for voice (low latency > perfect delivery)
Trade-off: Accept some packet loss for lower latency.
Senior
Q: Design a real-time multiplayer game network protocol. How do you balance reliability and latency? When would you use TCP vs UDP?
A:
Design: Hybrid Approach
class GameNetworkProtocol {
// Use UDP for game state (position, movement)
// Use TCP for critical events (player actions, chat)
// UDP: Fast, unreliable game state
async sendGameState(state: GameState): Promise<void> {
const packet = this.serializeState(state);
await this.udpSocket.send(packet, this.serverAddress);
// No retry - next update will replace this
}
// TCP: Reliable critical events
async sendPlayerAction(action: PlayerAction): Promise<void> {
await this.tcpSocket.write(this.serializeAction(action));
// Guaranteed delivery for important actions
}
// Hybrid: UDP with reliability for important state
async sendImportantState(state: GameState, sequence: number): Promise<void> {
// Send via UDP
await this.udpSocket.send(this.serializeState(state, sequence));
// Track for retry if needed
this.pendingStates.set(sequence, state);
// If no ACK received, retry via TCP
setTimeout(() => {
if (!this.acknowledged.has(sequence)) {
this.sendViaTCP(state, sequence);
}
}, 100);
}
}
Strategy:
- UDP for frequent updates: Position, movement (30-60 updates/sec)
- TCP for critical events: Player actions, chat, game events
- Sequence numbers: Detect missing UDP packets
- Selective reliability: Retry only important packets via TCP
- Compression: Reduce UDP packet size
Why this works:
- Low latency for game state (UDP)
- Reliability for critical events (TCP)
- Best of both worlds
Key Takeaways
- TCP: Reliable, ordered, connection-oriented - use when data integrity critical
- UDP: Fast, connectionless, unreliable - use when speed/latency critical
- Trade-off: Reliability vs Speed
- Choose based on use case: Web (TCP), Gaming (UDP), Video (UDP), File transfer (TCP)
- Hybrid approach: Use both - TCP for critical, UDP for frequent updates
- UDP reliability: Can add reliability on top of UDP if needed (QUIC does this)