system design

TCP, UDP, HTTP, gRPC, WebSockets: When to Use Each

Learn the difference between TCP, UDP, HTTP, gRPC, and WebSockets. Practical guide on picking the right protocol for your backend system.

By Akash Sharma·5 min read
#system design
#networking
#tcp
#grpc
#websockets
#backend
#protocols

Choosing the wrong protocol for your system can cause real problems. Build a chat app with HTTP polling and you'll hammer your servers. Use WebSockets for a simple REST API and you've over-engineered it.

Here's a practical guide to the main protocols and when to use each.

The Foundation: TCP vs UDP

All internet communication runs on one of two transport protocols.

TCP (Transmission Control Protocol) is reliable. Every packet is guaranteed to arrive, in order, exactly once. If a packet is lost, TCP retransmits it automatically. The trade-off: slightly more overhead and latency.

UDP (User Datagram Protocol) is fast but unreliable. Packets can arrive out of order, be duplicated, or get lost entirely. No retransmission. Your application handles errors.

plaintext
TCP: "Did you get packet 47?" → "Yes" → "Good, sending packet 48"
UDP: Just sends packets. Hope for the best.

Use TCP when: Data integrity matters — APIs, databases, file transfers, email. Use UDP when: Speed matters more than perfection — video streaming, gaming, voice calls, DNS.

Real example: Netflix video streaming can tolerate a few lost packets (you'll see a blip). Losing a credit card transaction cannot be tolerated.

HTTP: The Web Standard

HTTP is built on top of TCP. It's a request-response protocol — the client asks, the server answers.

HTTP/1.1: One request per connection at a time. Multiple connections needed for parallel requests. Still widely used.

HTTP/2: Multiple requests over a single connection simultaneously (multiplexing). Much faster for modern web apps. Supported by all major browsers and servers.

HTTP/3: Runs over QUIC instead of TCP. Faster connection setup. Better performance on unreliable networks (mobile). Increasingly adopted.

python
# A simple HTTP request — you use this all the time
import requests
response = requests.get("https://api.example.com/users/123")
data = response.json()

Use HTTP when: Building REST APIs, serving web pages, any standard client-server communication.

gRPC: High-Performance Service-to-Service

gRPC is built on HTTP/2 and uses Protocol Buffers (protobuf) for serialization instead of JSON.

Why does that matter? Protobuf is binary and much smaller than JSON. A JSON payload of 100 bytes might be 20 bytes as protobuf. At millions of requests per second, that adds up.

protobuf
// Define your service in a .proto file
service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}
 
message UserRequest {
  string user_id = 1;
}
 
message UserResponse {
  string name = 1;
  string email = 2;
}

gRPC also supports streaming — server streaming (server sends a stream of responses to one request), client streaming, and bidirectional streaming.

Use gRPC when:

  • Internal service-to-service communication (microservices)
  • You need high throughput with low latency
  • You're defining strict contracts between services
  • Multiple programming languages in your stack (gRPC is polyglot)

Don't use gRPC when: Your client is a browser (gRPC-Web exists but adds complexity) or you need human-readable debugging of requests.

WebSockets: Real-Time Two-Way Communication

HTTP is one direction at a time — client asks, server responds. WebSockets are different: once connected, both sides can send messages whenever they want.

plaintext
HTTP: Client → Request → Server → Response → done
WebSocket: Client ↔ Server (persistent, bidirectional)

A WebSocket connection starts as an HTTP request, then "upgrades" to a WebSocket connection.

python
# FastAPI WebSocket example
from fastapi import WebSocket
 
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"Echo: {data}")

Use WebSockets when:

  • Real-time features: chat, notifications, live feeds
  • Collaborative tools (Google Docs-style editing)
  • Live dashboards, trading platforms
  • Multiplayer games

Don't use WebSockets when: You just need regular request-response APIs. WebSockets are stateful and harder to scale.

Server-Sent Events (SSE): Server Push Made Simple

SSE is a one-way streaming protocol — server sends updates to the client over HTTP. Simpler than WebSockets when you only need server-to-client updates.

python
# FastAPI SSE example
from fastapi.responses import StreamingResponse
 
async def event_stream():
    for i in range(10):
        yield f"data: Update {i}\n\n"
        await asyncio.sleep(1)
 
@app.get("/stream")
async def stream():
    return StreamingResponse(event_stream(), media_type="text/event-stream")

Use SSE when: Server needs to push updates to clients (notifications, progress bars, live scores) and clients don't need to send data back.

Quick Decision Guide

SituationProtocol
REST API, standard web requestsHTTP/2
Service-to-service in microservicesgRPC
Chat, live collaboration, gamingWebSockets
Server pushing updates (notifications)SSE
Video streaming, voice, DNSUDP
Anything needing reliabilityTCP

Key Takeaways

  • TCP is reliable; UDP is fast but lossy — pick based on whether data loss is acceptable
  • HTTP is the standard for APIs; HTTP/2 is better than HTTP/1.1 for parallel requests
  • gRPC is HTTP/2 + protobuf — faster and more structured than REST for internal services
  • WebSockets are for real-time bidirectional communication
  • SSE is simpler than WebSockets when you only need server-to-client streaming

The right protocol for your use case can meaningfully impact latency, scalability, and developer experience.

Related reading: OSI Model Explained · DNS Explained

Enjoyed this article?

Get weekly insights on backend architecture, system design, and Go programming.