TCP, HTTP & Web cho Backend/TCP vs UDP — khi nào bỏ độ tin cậy đổi tốc độ
6/29
Bài 6 / 29~18 phútTCP & UDP Deep DiveMiễn phí lượt xem

TCP vs UDP — khi nào bỏ độ tin cậy đổi tốc độ

UDP gửi-và-quên, header chỉ 8 byte, không bắt tay — DNS, VoIP, game chọn nó vì trễ chết người hơn mất gói. QUIC xây lại reliability trên UDP để kiểm soát ở user-space.

TL;DR: UDP (User Datagram Protocol — RFC 768) là giao thức "gửi-và-quên": không handshake, không ACK, không đảm bảo thứ tự, không congestion control — header chỉ 8 byte so với 20 byte của TCP. Sự khác biệt cốt lõi là tradeoff: TCP đảm bảo mọi byte tới đủ và đúng thứ tự nhưng tốn RTT cho bắt tay và retransmission khi mất gói; UDP không đảm bảo gì nhưng byte đến đích ngay lập tức hoặc không bao giờ, không bị giữ lại chờ retransmit. DNS, VoIP, game realtime chọn UDP vì mất một gói nhỏ chấp nhận được, còn trễ thì không. QUIC (RFC 9000) — nền của HTTP/3 — xây lại reliability và congestion control ngay trên UDP để kiểm soát ở user-space, tránh phụ thuộc vào TCP trong kernel.

Bạn đã hiểu TCP cung cấp những đảm bảo gì (bài 0102). Bài này hỏi câu ngược: khi nào những đảm bảo đó là gánh nặng thay vì lợi ích?

1. Analogy — Bưu kiện đảm bảo vs tờ rơi

Hình dung bạn cần gửi thứ gì đó:

  • Gửi bưu kiện có bảo đảm (TCP): bưu điện ghi nhận từng kiện, yêu cầu người nhận ký xác nhận, gửi lại nếu không nhận được. Hoàn hảo cho hợp đồng pháp lý. Nhưng mỗi bước tốn thời gian — nếu bạn cần thông tin đến tay 1 000 người trong 1 giây, đây là sai lựa chọn.
  • Rải tờ rơi từ máy bay (UDP): tờ rơi bay về các hướng, một số rơi xuống đúng đích, một số bay đi — không ai theo dõi hay gửi lại. Nhưng 1 000 tờ rơi phát trong 1 giây. Ổn thôi nếu nội dung từng tờ không quan trọng bằng việc thông tin đến ngay.
Bưu kiện / tờ rơiTCP / UDP
Ký nhận, gửi lại nếu thất lạcACK + retransmission
Tốn thời gian chờ xác nhận1 RTT handshake + RTT mỗi lần retransmit
Tờ rơi mất một tờ — không saoMất 1 gói UDP — ứng dụng tự quyết xử lý
Tờ rơi đến ngay, không chờByte UDP đến đích trong một lần, không xếp hàng
💡 Cách nhớ

TCP = đảm bảo có giá. UDP = nhanh không đảm bảo. Chọn TCP khi dữ liệu không thể thiếu; chọn UDP khi dữ liệu cũ hơn là vô dụng.

2. Header UDP — 8 byte, không hơn

UDP (User Datagram Protocol) được định nghĩa trong RFC 768 (1980) — chỉ 3 trang. Header chỉ có 4 trường:

 0      7 8     15 16    23 24    31
+--------+--------+--------+--------+
|   Source Port   |  Destination Port|
+--------+--------+--------+--------+
|     Length      |    Checksum      |
+--------+--------+--------+--------+
|          data octets ...
  • Source Port / Destination Port (mỗi cái 16-bit): xác định ứng dụng gửi/nhận — giống TCP.
  • Length (16-bit): tổng độ dài UDP header + data.
  • Checksum (16-bit): kiểm tra lỗi cơ bản (tuỳ chọn với IPv4, bắt buộc với IPv6).

Tổng cộng: 8 byte. So với TCP tối thiểu 20 byte (chưa kể TCP options). Với gói DNS nhỏ (~60 byte data), overhead TCP cao hơn 160% so với UDP.

3. Vì sao TCP gây hại cho realtime — head-of-line blocking

Đây là cơ chế ít được giải thích nhưng quan trọng nhất để hiểu tại sao game/VoIP không thể dùng TCP.

TCP đảm bảo thứ tự byte. Nếu gói thứ 5 bị mất, TCP giữ lại gói thứ 6, 7, 8... chờ gói 5 được retransmit thành công rồi mới giao cho ứng dụng — dù ứng dụng đã nhận được 6, 7, 8. Hiện tượng này gọi là head-of-line blocking (HOL blocking — một gói mất chặn cả hàng sau).

Với stream video:

  • Frame 100 bị mất → TCP block frame 101, 102, 103...
  • Retransmission mất 1 RTT (50-150ms xuyên lục địa)
  • Khi frame 100 đến thì frame 101 đã lỗi thời 150ms
  • Người xem thấy video đứng hình rồi giật cục — tệ hơn bỏ luôn frame 100

Với UDP:

  • Frame 100 bị mất → ứng dụng nhận frame 101, 102, 103 ngay
  • Ứng dụng hiển thị frame 101 (bỏ qua 100) — người xem thấy 1 frame giật nhẹ
  • Tốt hơn nhiều về trải nghiệm
sequenceDiagram
    participant S as Server
    participant N as Mang (co mat goi)
    participant C as Client app

    Note over S,C: TCP — HOL blocking
    S->>N: Frame 100
    S->>N: Frame 101
    N--xC: Frame 100 (mat)
    Note over C: Giu 101, cho 100
    S->>N: Retransmit Frame 100
    N-->>C: Frame 100 (tre 1 RTT)
    Note over C: Giao 100+101 (da cu)

    Note over S,C: UDP — khong block
    S->>C: Frame 100
    S->>C: Frame 101
    Note over C: 100 mat, giao 101 ngay

4. Use case thực tế — ai chọn giao thức nào và vì sao

DNS — UDP port 53
Query nhỏ (~60-100 byte), response trong 1 gói, không cần đảm bảo thứ tự. Nếu mất thì resolver gửi lại ngay (không cần TCP retransmit). Handshake TCP tốn gấp đôi thời gian cho một câu hỏi-trả lời đơn giản. DNS dùng TCP chỉ khi response vượt 512 byte (zone transfer, DNSSEC lớn).
VoIP / Video call — RTP/UDP
RTP (Real-time Transport Protocol) chạy trên UDP. Mất 1 frame âm thanh (20ms) không ai nghe thấy, nhưng trễ vượt 150ms thì cuộc hội thoại bị phá vỡ. Retransmission TCP mất 1 RTT chờ đợi — không thể chấp nhận.
Game realtime — custom UDP
Game FPS gửi trạng thái nhân vật 60 lần/giây. Vị trí 100ms trước không còn giá trị — thà nhận vị trí mới nhất ngay còn hơn vị trí cũ đúng thứ tự. Game engine tự xây reliable channel riêng trên UDP cho thứ quan trọng (điểm số, sự kiện quan trọng), dùng unreliable UDP cho vị trí.
DHCP — UDP port 67/68
Client chưa có IP, không thể thiết lập TCP. DHCP Discover broadcast trên UDP — giao thức duy nhất hoạt động được trước khi có địa chỉ IP.
HTTP/3 — QUIC/UDP port 443
QUIC (RFC 9000) xây reliability + congestion control trên UDP. Vì sao không dùng TCP thẳng? Để tránh HOL blocking ở tầng TCP (multiplexing nhiều stream độc lập) và để cập nhật giao thức ở user-space thay vì phải thay kernel.

5. Cây quyết định chọn giao thức

flowchart TD
    A["Du lieu co the mat khong?"] -->|"KHONG — phai den du"| B["Dung TCP"]
    A -->|"CO — mat it van ok"| C["Thu tu quan trong?"]
    C -->|"KHONG"| D["Tre quan trong hon mat goi?"]
    C -->|"CO"| E["Su kien quan trong (game, VoIP signal)"]
    E --> F["UDP + tu xu ly thu tu / ACK rieng"]
    D -->|"CO — realtime"| G["UDP (VoIP, game state, DNS)"]
    D -->|"KHONG"| H["Xem xet TCP hoac UDP + application retry"]
    B --> I["File transfer, HTTP, database, email"]
    G --> J["Ung dung tu xu ly mat goi neu can"]

Heuristic đơn giản:

Đặc điểm dữ liệuChọn
Phải đến đủ, đúng thứ tựTCP
Realtime, trễ chết người hơn mấtUDP
Nhỏ, 1 round-trip (query/response)UDP
Cần reliability + nhiều stream độc lậpQUIC (UDP)
Không biết → mặc địnhTCP

6. QUIC — xây lại reliability trên UDP

QUIC (Quick UDP Internet Connections, RFC 9000, 2021) là giao thức nền của HTTP/3. Thay vì dùng TCP, QUIC xây trên UDP và tự xây các tính năng của TCP ở user-space.

Vì sao không dùng TCP thẳng cho HTTP/2? HTTP/2 multiplex nhiều HTTP request vào 1 TCP connection, nhưng nếu 1 gói TCP bị mất, tất cả stream bị block (HOL blocking ở tầng TCP). QUIC xử lý từng stream độc lập — mất gói ở stream A không chặn stream B.

Vì sao xây trên UDP, không phải giao thức mới hoàn toàn? Vì:

  1. Firewall và middlebox đã sẵn sàng cho UDP port 443 — giao thức mới ở tầng 4 sẽ bị block.
  2. Cập nhật không cần thay kernel — QUIC chạy trong user-space (trong app hoặc TLS library), có thể phát hành phiên bản mới như cập nhật phần mềm thường.
📌 QUIC không phải UDP thuần

QUIC tự xây: connection establishment (0-RTT/1-RTT), reliable delivery per-stream, congestion control (CUBIC/BBR), mã hoá bắt buộc (TLS 1.3 tích hợp). Nói "HTTP/3 dùng UDP" không sai, nhưng QUIC là một giao thức phức tạp chạy trên UDP — không phải "bỏ đảm bảo đi". HTTP/3 và QUIC sẽ được mổ kỹ ở module 2.

7. Pitfall — hiểu nhầm thường gặp

Nhầm 1: "UDP không tin cậy nghĩa là không nên dùng trong production." ✅ UDP là lựa chọn đúng đắn cho các use case phù hợp. DNS — backbone của Internet — chạy trên UDP. Mọi video call, game online đang hoạt động đều dùng UDP. "Không tin cậy" nghĩa là giao thức không tự retransmit — ứng dụng quyết định có cần retry không và retry như thế nào.

Nhầm 2: "Dùng TCP cho streaming video là an toàn hơn vì đảm bảo mọi frame đến đích." ✅ Với video streaming live (WebRTC, RTSP), TCP gây ra trải nghiệm tệ hơn do HOL blocking và buffer bloat — frame cũ đến muộn làm video giật cục. Frame mất còn tốt hơn frame trễ. Video on-demand (YouTube, Netflix adaptive streaming) thì khác — buffer đủ lớn nên TCP ổn.

Nhầm 3: "UDP không có checksum nên dữ liệu có thể sai mà không biết." ✅ UDP có trường Checksum 16-bit trong header. Với IPv6 checksum bắt buộc; với IPv4 tuỳ chọn nhưng hầu hết stack mạng bật mặc định. Điều UDP không làm là retransmit khi detect lỗi — nó chỉ drop gói lỗi và thông báo (hoặc không thông báo) cho ứng dụng. Lớp application có thể tự thêm mã sửa lỗi (FEC) nếu cần.

8. Liên hệ các bài khác

  • Bài 01 — TCP 3-way handshake: handshake 3 bước là chi phí cố định của TCP — bài đó giải thích vì sao cần 1 RTT trước khi gửi byte đầu. UDP không có chi phí này.
  • Bài 02 — TCP reliability: sequence number, ACK, retransmission là những thứ UDP không có — đọc để hiểu rõ bạn đang bỏ gì khi chọn UDP.
  • Bài 03 — TCP flow & congestion control: congestion control (slow start, AIMD) là thứ UDP cũng không có — game/VoIP trên UDP phải tự xử lý hoặc chấp nhận là aggressive.
  • Module 2 bài 05 — HTTP/3 & QUIC: QUIC được đề cập ở đây như "reliability trên UDP" — module 2 mổ kỹ cách QUIC thực sự hoạt động, 0-RTT connection, multiplexing per-stream.

9. 📚 Deep Dive — tài liệu gốc

📚 RFC & spec chính thức

Đọc khi muốn đi tới gốc:

10. Tóm tắt

  • UDP (RFC 768) là giao thức "gửi-và-quên": không handshake, không ACK, không thứ tự đảm bảo, không congestion control — header chỉ 8 byte.
  • Đánh đổi cốt lõi: UDP không trễ vì retransmission; TCP không mất dữ liệu nhưng tốn RTT cho handshake và block khi có gói mất (HOL blocking).
  • HOL blocking là lý do TCP không phù hợp cho realtime: mất 1 gói block tất cả gói sau cho tới khi retransmit xong.
  • Use case UDP: DNS (query nhỏ, 1 RTT), VoIP/video (mất frame chấp nhận, trễ không), game realtime (state cũ vô dụng), DHCP (chưa có IP).
  • QUIC (RFC 9000) xây reliability + congestion control trên UDP ở user-space, giải quyết HOL blocking của TCP — nền tảng của HTTP/3.
  • Chọn TCP khi dữ liệu phải đến đủ; chọn UDP khi dữ liệu cũ hơn là vô dụng hoặc latency quan trọng hơn completeness.

11. Tự kiểm tra

Tự kiểm tra
Q1
Vì sao header UDP chỉ có 8 byte trong khi TCP tối thiểu 20 byte?
UDP không cần lưu trữ thông tin cho các tính năng TCP có: không có sequence number (không đảm bảo thứ tự), không có ACK number (không xác nhận nhận được), không có window size (không flow control), không có flags cho state machine (không handshake). UDP chỉ cần 4 trường: source port, destination port, length, checksum — đủ để gửi datagram tới đúng ứng dụng đích và kiểm tra lỗi cơ bản. Thiết kế tối giản này là điểm mạnh, không phải điểm yếu — cho phép gửi nhanh với overhead tối thiểu.
Q2
HOL blocking là gì và vì sao nó làm TCP không phù hợp cho VoIP?
Head-of-line blocking là hiện tượng: khi một gói TCP bị mất, TCP giữ lại tất cả gói nhận được sau đó (dù đã nhận xong) và chờ retransmission của gói bị mất trước khi giao cho ứng dụng. Với VoIP, frame âm thanh được gửi mỗi 20ms. Nếu frame bị mất và phải chờ retransmit (~1 RTT = 50-200ms), frame đó đến tay ứng dụng đã trễ ít nhất 50ms và không còn dùng được. Tệ hơn, các frame tiếp theo cũng bị delay cùng. UDP cho phép ứng dụng nhận frame tiếp theo ngay — bỏ qua frame mất còn tốt hơn chờ frame trễ.
Q3
DNS query dùng UDP thay vì TCP. Vì sao điều này hợp lý?
DNS query là dạng request-response một chiều rất nhỏ (thường dưới 512 byte, vừa trong 1 UDP datagram). Dùng TCP đòi hỏi 3-way handshake (~1 RTT) rồi mới gửi được query — gần như gấp đôi thời gian cho một thao tác đơn giản. Với UDP, query gửi ngay, response về ngay — DNS resolver có thể gửi lại query nếu không nhận response trong timeout (thường 200-500ms). Nếu response lớn hơn 512 byte (zone transfer, DNSSEC), DNS mới fallback sang TCP — đây là lý do DNS server phải listen cả hai giao thức trên port 53.
Q4
QUIC chạy trên UDP nhưng vẫn có reliability. Điều này không mâu thuẫn sao? Vì sao không dùng TCP thẳng?
Không mâu thuẫn — UDP là lớp transport dưới cùng, còn reliability là tính năng được xây ở tầng trên. QUIC (RFC 9000) tự cài đặt: reliable delivery per-stream, congestion control, connection establishment — tất cả trong user-space. Không dùng TCP thẳng vì hai lý do: (1) TCP gây HOL blocking khi multiplexing nhiều stream — mất gói ở stream A block stream B; QUIC xử lý từng stream độc lập. (2) Cập nhật QUIC không cần thay kernel — deploy như cập nhật phần mềm thường.
Q5
Game FPS gửi vị trí nhân vật 60 lần/giây qua UDP. Vì sao không dùng TCP cho chắc ăn?
Dữ liệu vị trí nhân vật ở thời điểm tvô dụng ở thời điểm t+100ms — không cần đến đúng thứ tự hay đến đủ. Nếu dùng TCP: mất 1 gói vị trí → TCP block gói vị trí tiếp theo → ứng dụng không nhận được vị trí mới trong khi chờ retransmit → nhân vật trên màn hình đóng băng rồi giật cục. Với UDP: mất 1 gói → bỏ qua → nhận gói vị trí mới ngay → nhân vật dịch chuyển mượt. Game engine thường tự xây một lớp reliable channel nhỏ trên UDP cho các sự kiện quan trọng (hit detection, sự kiện game), còn vị trí/góc nhìn vẫn unreliable UDP.
Q6
Bạn cần chọn giao thức cho một hệ thống telemetry gửi metrics từ 10 000 sensor mỗi giây. Metrics có thể mất vài điểm nhưng không cần exact ordering. Chọn gì và vì sao?
Chọn UDP. Metrics telemetry có đặc điểm: (1) volume lớn, mất vài điểm dữ liệu không ảnh hưởng phân tích tổng thể; (2) thứ tự không quan trọng — timestamp trong payload đủ để sort; (3) TCP overhead (handshake × 10 000 sensor) và congestion control gây queue delay không cần thiết. Nhiều hệ thống monitoring (StatsD, collectd) dùng UDP chính vì lý do này. Nếu muốn đảm bảo hơn, UDP + application-level batching (gộp nhiều metrics vào 1 datagram) là pattern phổ biến.
Q7
Vì sao DHCP phải dùng UDP thay vì TCP?
DHCP (Dynamic Host Configuration Protocol) dùng UDP vì một lý do không thể tránh: khi client gửi DHCP Discover, client chưa có địa chỉ IP. TCP yêu cầu kết nối được thiết lập giữa hai địa chỉ IP cụ thể — nhưng client chưa có IP, nên không thể thiết lập TCP connection. UDP cho phép gửi broadcast (255.255.255.255) từ địa chỉ nguồn 0.0.0.0 — cơ chế duy nhất hoạt động được trước khi client có địa chỉ. Đây là ví dụ UDP là lựa chọn duy nhất, không phải chỉ là lựa chọn tốt hơn.

Bài tiếp theo: Connection pooling & keep-alive — vì sao app cần pool

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Connection pooling & keep-alive — vì sao app cần pool