🌐 Network✍️ Khoa📅 19/04/2026☕ 9 phút đọc

Network: TCP/IP và HTTP nền tảng

1. OSI Model & TCP/IP Stack

OSI 7 tầng vs TCP/IP 4 tầng

OSI Model                TCP/IP Model        Giao thức thực tế
─────────────────────────────────────────────────────────────
7. Application  ┐
6. Presentation ├──►  Application      HTTP, HTTPS, DNS, gRPC
5. Session      ┘
4. Transport    ────►  Transport        TCP, UDP, QUIC
3. Network      ────►  Internet         IP, ICMP, BGP
2. Data Link    ┐
1. Physical     ┴──►  Network Access   Ethernet, WiFi, Fiber

OSI là mô hình lý thuyết được dạy trong sách giáo khoa — hữu ích để phân tích, nhưng không ai "implement" đúng 7 tầng đó. TCP/IP là thứ thực sự chạy trên internet, với 4 tầng gọn hơn.

Dữ liệu đi qua các tầng như thế nào?

Mỗi tầng chỉ nói chuyện với tầng ngay cạnh nó. Khi gửi, mỗi tầng bọc thêm một lớp header vào ngoài dữ liệu — gọi là encapsulation. Khi nhận, quá trình ngược lại: mỗi tầng bóc lớp header của mình ra rồi chuyển lên trên.

Gửi:  Data → [App header | Data]                      (Application)
           → [TCP header | App header | Data]          (Transport)
           → [IP header  | TCP header | Data]          (Network)
           → [ETH header | IP header  | TCP | Data]    (Data Link)
           → bits trên dây                             (Physical)

Nhận: Quá trình ngược lại — mỗi tầng bóc header của mình ra

2. TCP — Chi tiết kỹ thuật

TCP Header format

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
┌─────────────────────────┬─────────────────────────────────────┐
│      Source Port        │         Destination Port            │
├─────────────────────────┴─────────────────────────────────────┤
│                      Sequence Number                          │
├───────────────────────────────────────────────────────────────┤
│                   Acknowledgment Number                       │
├────────┬───────┬─┬─┬─┬─┬─┬─┬──────────────────────────────────┤
│Data Off│Reserv │U│A│P│R│S│F│           Window Size            │
│  (4)   │  (6)  │R│C│S│S│Y│I│             (16 bits)            │
│        │       │G│K│H│T│N│N│                                  │
├────────────────┴─┴─┴─┴─┴─┴─┴──────────────────────────────────┤
│           Checksum          │        Urgent Pointer           │
└───────────────────────────────────────────────────────────────┘

3-Way Handshake

Trước khi trao đổi dữ liệu, TCP phải thiết lập kết nối qua 3 bước. Cả hai bên thỏa thuận sequence number ban đầu để theo dõi thứ tự packet.

Client                          Server
  │                               │
  │──── SYN (seq=x) ─────────────►│  "Tôi muốn kết nối, seq của tôi là x"
  │                               │
  │◄─── SYN-ACK (seq=y, ack=x+1) ─│  "OK, seq của tôi là y, tôi nhận được x"
  │                               │
  │──── ACK (ack=y+1) ───────────►│  "Tôi nhận được y, kết nối thành công"
  │                               │
  │         [Data Transfer]       │

Chi phí của handshake là 1 RTT (Round Trip Time) — client phải chờ trọn một vòng đi-về trước khi gửi được byte data đầu tiên.

4-Way Termination

Đóng kết nối cần 4 bước vì mỗi chiều phải đóng độc lập. Sau khi gửi ACK cuối, client chờ thêm 2×MSL (~60 giây) ở trạng thái TIME_WAIT để đảm bảo ACK đã tới nơi.

Client                          Server
  │──── FIN ────────────────────►│
  │◄─── ACK ─────────────────────│
  │◄─── FIN ─────────────────────│
  │──── ACK ────────────────────►│
  │    (wait 2×MSL = ~60s)       │  TIME_WAIT state

Flow Control & Congestion Control

TCP có hai cơ chế kiểm soát lưu lượng với mục tiêu khác nhau:

Flow Control — tránh làm quá tải receiver. Receiver thông báo còn bao nhiêu buffer trống qua trường Window Size trong header. Sender không được gửi vượt quá giá trị đó.

Congestion Control — tránh làm quá tải network. Đây là bài toán khó hơn vì không có ai thông báo — TCP phải tự suy luận từ việc mất gói.

Slow Start:
  cwnd = 1 MSS
  Mỗi ACK → cwnd × 2 (tăng theo hàm mũ)
  Đến ssthresh → chuyển sang Congestion Avoidance

Congestion Avoidance:
  cwnd tăng +1 MSS mỗi RTT (tăng tuyến tính)
  Khi mất gói → cwnd giảm mạnh → slow start lại

Linux dùng TCP Cubic mặc định; Google dùng BBR — cả hai đều implement ý tưởng trên nhưng với thuật toán tinh vi hơn.

Tại sao TCP tốn kém?

Mỗi kết nối mới:
  1. 3-way handshake    → 1 RTT
  2. TLS handshake      → 1-2 RTT thêm
  3. Slow start         → vài RTT để đạt throughput tối đa

Tổng: 3-4 RTT trước khi nhận được byte data đầu tiên!
→ Đây là lý do HTTP/2 và HTTP/3 ra đời

3. HTTP/1.1 — Nền tảng

Request/Response format (plaintext)

HTTP/1.1 là text thuần. Request và response đều có format cố định: dòng đầu (method/status), headers, rồi blank line, rồi body.

Request:
GET /api/users HTTP/1.1\r\n
Host: example.com\r\n
Accept: application/json\r\n
Authorization: Bearer token123\r\n
\r\n                          ← blank line kết thúc headers
[body nếu có]

Response:
HTTP/1.1 200 OK\r\n
Content-Type: application/json\r\n
Content-Length: 42\r\n
\r\n
{"id": 1, "name": "Alice"}

Vấn đề của HTTP/1.1

Head-of-Line (HOL) Blocking

HTTP/1.1 xử lý request theo thứ tự nghiêm ngặt: phải nhận xong response của request trước mới gửi được request tiếp. Nếu request đầu chậm, tất cả phải xếp hàng chờ.

Connection:  [REQ 1]──►[RES 1]──►[REQ 2]──►[RES 2]──►[REQ 3]──►[RES 3]
                       ↑
              Phải chờ RES 1 xong mới gửi REQ 2 được

HTTP pipelining cho phép gửi nhiều request liên tiếp mà không chờ response, nhưng response vẫn phải về đúng thứ tự — nên HOL blocking vẫn còn. Hầu hết browser đã vô hiệu hóa tính năng này.

Header dư thừa

Mỗi request gửi lại toàn bộ headers, kể cả những thứ không bao giờ đổi như User-Agent, Cookie, Authorization. Với application nhiều request, đây là lãng phí bandwidth đáng kể.

Workarounds thời HTTP/1.1

Vì browser chỉ mở tối đa 6 connection/domain, dev time đó phải dùng các trick:

  • Domain Sharding: tách static files sang cdn1.example.com, cdn2.example.com... để có thêm kết nối. Cồng kềnh và tốn thêm DNS lookup + TCP handshake overhead.
  • Bundling/Spriting: gộp nhiều JS/CSS thành một file to, gộp nhiều icon thành một sprite image. Không thanh lịch và gây khó khăn cho cache invalidation.

4. HTTP/2 — Binary, Multiplexing, HPACK

RFC 7540 (2015). HTTP/2 giữ nguyên semantics của HTTP/1.1 (cùng methods, status codes, headers) nhưng thay đổi hoàn toàn cách truyền dữ liệu trên wire.

4.1 Binary Framing Layer

HTTP/1.1 là plaintext — con người đọc được, nhưng máy phải parse chậm. HTTP/2 chuyển sang binary frames: cấu trúc cố định, parse nhanh hơn, ít lỗi hơn.

HTTP/2 binary frame:
  ┌──────────────────────────────────────────┐
  │          Frame Format                    │
  ├────────────┬──────────┬──────────────────┤
  │ Length (24)│Type (8)  │  Flags (8)       │
  ├────────────┴──────────┴──────────────────┤
  │ R │         Stream Identifier (31 bits)  │
  ├───┴──────────────────────────────────────┤
  │              Frame Payload               │
  └──────────────────────────────────────────┘

Frame Types

Type ID Mô tả
DATA 0x0 Chứa body của request/response
HEADERS 0x1 Chứa headers (compressed bằng HPACK)
PRIORITY 0x2 Đặt độ ưu tiên stream
RST_STREAM 0x3 Hủy một stream
SETTINGS 0x4 Thay đổi cấu hình connection
PUSH_PROMISE 0x5 Server push
PING 0x6 Kiểm tra connection còn sống
GOAWAY 0x7 Đóng connection
WINDOW_UPDATE 0x8 Flow control
CONTINUATION 0x9 Tiếp tục HEADERS frame

4.2 Streams & Multiplexing

Đây là cải tiến lớn nhất của HTTP/2. Thay vì mỗi request cần một TCP connection riêng, HTTP/2 dùng khái niệm stream — một cặp request/response logic chạy trên cùng một TCP connection.

HTTP/2 connection = 1 TCP connection chứa nhiều STREAMS song song

Stream IDs:
  Client-initiated: 1, 3, 5, 7... (số lẻ)
  Server-initiated: 2, 4, 6, 8... (số chẵn, dùng cho server push)

Ví dụ trên cùng 1 TCP connection:
  Stream 1: GET /index.html    ─────────────────►
  Stream 3: GET /style.css     ───────────────────►
  Stream 5: GET /app.js            ────────────────►
  Stream 7: POST /api/data     ─────►
                               (tất cả đồng thời!)

Frame interleaving trên wire:
  [S1:HEADERS][S3:HEADERS][S5:HEADERS][S1:DATA][S3:DATA][S7:HEADERS][S5:DATA]...

Kết quả là không còn cần domain sharding hay bundling — browser có thể gửi hàng chục requests song song trên 1 connection.

HTTP/1.1:  [====REQ1====][====REQ2====][====REQ3====]  (tuần tự)

HTTP/2:    [=REQ1=]
           [===REQ2===]                                 (song song)
           [==REQ3==]

⚠️ HTTP/2 giải quyết HOL blocking ở application layer nhưng vẫn còn HOL blocking ở TCP layer. Nếu 1 TCP packet bị mất → tất cả streams phải chờ retransmission. HTTP/3 mới giải quyết triệt để vấn đề này.

4.3 HPACK — Header Compression

RFC 7541. HPACK giải quyết vấn đề header dư thừa bằng 3 kỹ thuật kết hợp.

1. Static Table — 61 entries được định nghĩa sẵn trong spec. Các header phổ biến như :method: GET hay :path: / chỉ cần gửi một index 1-2 byte thay vì full string.

Index │ Header Name          │ Header Value
──────┼──────────────────────┼──────────────
  2   │ :method              │ GET
  4   │ :path                │ /
  8   │ :status              │ 200
  ...

→ "GET /" chỉ cần gửi index 2 và index 4 — 2 bytes thay vì 14 bytes!

2. Dynamic Table — được xây dựng trong quá trình kết nối. Lần đầu gửi header mới thì gửi full string và thêm vào bảng. Lần sau chỉ cần gửi index.

Lần đầu: Authorization: Bearer abc123  → gửi full + thêm vào index 62
Lần sau: → chỉ gửi index 62 — 1-3 bytes thay vì 28+ bytes!

3. Huffman Encoding — thay ASCII 8-bit bằng Huffman code: ký tự hay gặp dùng ít bit hơn, ký tự hiếm dùng nhiều bit hơn. Trung bình giảm ~30% kích thước headers.

4.4 Stream Priority

Stream có thể được tổ chức thành dependency tree với weight. Bandwidth được phân bổ theo tỉ lệ weight — ví dụ: HTML trước, CSS/JS sau, images cuối.

        Stream 1 (weight=16)
       /          \
  Stream 3        Stream 5 (weight=32)
  (weight=16)    /          \
            Stream 7      Stream 9
            (weight=8)    (weight=16)

4.5 Server Push

Server có thể chủ động gửi resources mà không cần client request. Ví dụ khi client hỏi index.html, server biết client sẽ cần style.cssapp.js nên push luôn.

Trong thực tế tính năng này ít hiệu quả vì server không biết client đã cache gì. Chrome đã deprecated HTTP/2 push năm 2022.

4.6 Connection Preface

Client bắt đầu HTTP/2 connection bằng:
  PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n   ← "magic bytes"
  + SETTINGS frame

Server response:
  + SETTINGS frame
  + SETTINGS ACK