Protocol và Transport trong asyncio

asyncio cung cấp hai API ở hai mức trừu tượng cho lập trình mạng: streams (high-level, async/await thuần) và protocols/transports (low-level, callback-based). Cặp ProtocolTransport tách rõ trách nhiệm theo mô hình OSI — Transport lo việc truyền byte qua socket (tương đương tầng 4), Protocol lo ngữ nghĩa giao thức ở tầng ứng dụng (tầng 7). Tách biệt này cho phép cùng một Protocol chạy trên nhiều loại transport (TCP, SSL, pipe, subprocess stdio) mà không sửa code, và ngược lại cùng một transport phục vụ nhiều protocol khác nhau.

flowchart TB
    L["asyncio Event loop"] --> T["Transport - tầng 4<br/>(TCP, SSL, pipe, subprocess)"]
    T --> P["Protocol - tầng 7<br/>(HTTP, WebSocket, gRPC...)"]
    P -->|on data| H["Application logic"]
    H -->|write/close| T
    T --> S[("OS socket / fd")]

Hợp đồng giữa Transport và Protocol

Transport đẩy dữ liệu vào Protocol qua callback, và nhận lệnh ghi từ Protocol qua phương thức. Bốn callback chính của asyncio.Protocol:

Hai callback bổ sung cho flow control là pause_writing()resume_writing() — Transport gọi khi buffer của OS chạm high/low water mark. Application phải tôn trọng để giữ bộ nhớ không phình; chi tiết xem backpressure ở tầng transport.

Phía Protocol gọi vào Transport bằng transport.write(data), transport.close(), transport.get_extra_info("socket") để truy cập metadata. Không có ngược lại — Protocol không tự đọc từ Transport, mà nhận data qua data_received.

class EchoProtocol(asyncio.Protocol):
    def connection_made(self, transport):
        self.transport = transport
    def data_received(self, data):
        self.transport.write(data)  # echo
    def connection_lost(self, exc):
        ...

server = await loop.create_server(
    protocol_factory=EchoProtocol,
    sock=sock,
)

protocol_factory là một callable trả về Protocol instance mới cho mỗi connection — event loop tự gọi mỗi khi accept() thành công. Pattern này tránh state chia sẻ giữa các connection.

Vì sao tách Protocol và Transport

Trước asyncio, mô hình tương tự xuất hiện ở Twisted (giai đoạn 2002+) — Glyph Lefkowitz coi tách biệt này là một trong những bài học quan trọng nhất về thiết kế thư viện mạng. Lợi ích cụ thể:

Khi nào dùng streams thay vì Protocol/Transport

asyncio streams (asyncio.start_server, StreamReader, StreamWriter) là wrapper async/await trên Protocol/Transport. Dùng streams khi:

Dùng trực tiếp Protocol/Transport khi:

Dẫn chứng từ uasgi

uasgi cài đặt H11Protocol (HTTP/1.1) và H2Protocol (HTTP/2) đều là subclass của asyncio.Protocol. data_received của H11Protocol feed bytes vào httptools parser; parser emit callback on_message_begin, on_header, on_headers_complete, on_body, on_message_complete cho protocol xử lý. Bytes ngược lại được ghi qua transport.write đã lưu từ connection_made. Đây là minh hoạ trực tiếp pattern hợp đồng giữa Transport và Protocol.

Cập nhật: 2026-05-29