Mạng cho lập trình viên
Sê-ri 45 bài về mạng máy tính nhìn từ góc lập trình viên: mỗi phần dựng lại một hiện tượng mạng trong container rồi đo bằng số thật.
45/45 phần đã đăng
Mạng
1
Gói tin từng tầng
Mỗi gói tin gánh 66 byte header cố định. Với 1 byte dữ liệu, 98,51% những gì chạy trên dây là header — đo thật bằng tcpdump.
02/09/2026
· 7 phút đọc
2
Địa chỉ IP và subnet
Subnet mask, chứ không phải địa chỉ, mới vẽ ra biên mạng con. Cùng một IP có thể trên-link hay ngoài-link tùy prefix — đo thật bằng ip route get.
02/09/2026
· 7 phút đọc
3
ARP trên mạng ảo
Trước khi gửi gói đầu tiên tới một hàng xóm, máy phải hỏi MAC bằng một vòng ARP tốn ~190 micro giây — trả một lần rồi cache. Đo thật bằng tcpdump.
02/09/2026
· 7 phút đọc
4
Bắt tay TCP ba bước
Mở một kết nối TCP tốn ba gói và một vòng khứ hồi trước khi gửi được byte đầu tiên. Đo thật chuỗi SYN, SYN-ACK, ACK bằng tcpdump.
02/09/2026
· 7 phút đọc
5
TCP so với UDP
Dưới mất gói 10%, TCP giao đủ 100% nhưng đuôi trễ vọt lên 200-800 ms vì phát lại; UDP giữ trễ thấp đều nhưng rơi ~10%. Đo thật bằng netem.
02/09/2026
· 8 phút đọc
6
Cổng và socket
Một máy khách không mở được 65535 kết nối tới một cổng — trần thật là số cổng tạm (~28232), và nó đếm theo từng bộ bốn. Đo thật bằng container.
02/09/2026
· 8 phút đọc
7
MTU và phân mảnh
Vượt MTU thì gói hoặc bị cắt thành mảnh, hoặc bị chặn thẳng nếu đặt cờ DF. Và mất 10% gói có thể thành mất 37% datagram — đo thật bằng container.
02/09/2026
· 9 phút đọc
8
RTT và thông lượng
Với cửa sổ cố định, thông lượng TCP tỉ lệ nghịch với RTT — RTT gấp đôi thì tốc độ còn một nửa. Đo thật bằng netem, và một cái bẫy slow-start.
02/09/2026
· 8 phút đọc
9
Cửa sổ TCP
Ở RTT cố định, thông lượng tăng tỉ lệ với cửa sổ cho tới khi đụng trần băng thông rồi bằng phẳng. Điểm gãy đúng bằng tích băng thông-độ trễ — đo thật.
02/09/2026
· 8 phút đọc
10
Kiểm soát tắc nghẽn
Chỉ 1% mất gói làm sập thông lượng CUBIC khoảng 30 lần, để 97% đường ống bỏ không — vì giao thức đọc mọi gói rơi thành 'tắc'. Đo thật bằng netem.
02/09/2026
· 9 phút đọc
11
Đệm gửi và nhận
send() nuốt được khoảng 1,5 lần tổng hai đệm socket rồi mới chặn, dù người nhận không đọc byte nào. Đó là kiểm soát luồng của TCP — đo thật.
02/09/2026
· 8 phút đọc
12
Nagle và ACK trễ
Một vòng request/response lẽ ra vài chục micro giây có thể kẹt đúng 40 ms vì Nagle và delayed ACK chờ nhau. Đo thật, và cách nó ẩn hiện.
03/09/2026
· 8 phút đọc
13
Trạng thái TIME_WAIT
Bên đóng kết nối trước phải nằm TIME_WAIT đúng 60 giây trên Linux — và tcp_fin_timeout KHÔNG chỉnh được con số đó. Đo thật ai gánh, bao lâu.
03/09/2026
· 8 phút đọc
14
Keepalive TCP
Một kết nối có đầu kia biến mất im lặng: mặc định TCP mất hơn 2 giờ mới phát hiện. Bật keepalive tuỳ chỉnh thì xuống vài giây — đo thật công thức IDLE + CNT×INTVL.
03/09/2026
· 8 phút đọc
15
Phân giải DNS
Một getaddrinfo gửi HAI truy vấn (A và AAAA) và chờ cả hai; nếu AAAA bị bỏ rơi, lookup stall ~6 giây dù IPv4 sẵn ngay. Đo thật, kèm chuyện OS không cache DNS.
03/09/2026
· 7 phút đọc
16
TTL của DNS
Một bản ghi DNS được đệm đúng bằng TTL giây rồi mới hỏi lại nguồn — và đổi bản ghi ở nguồn chỉ có hiệu lực sau khi bản sao đệm hết hạn. Đo thật từng giây.
03/09/2026
· 8 phút đọc
17
DNS đệ quy
Một tra cứu nguội đi ba vòng — root, TLD, thẩm quyền — mỗi vòng một khứ hồi; đệm chuỗi ủy quyền để tên kế cùng đuôi chỉ còn 2 vòng. Đo thật bằng cây DNS tự dựng.
03/09/2026
· 7 phút đọc
18
HTTP một request
Một GET trên kết nối mới tốn 2 vòng khứ hồi (bắt tay + trao đổi), tái dùng còn 1; và với thân 40 byte, header chiếm ~78% response. Đo thật bằng tcpdump.
03/09/2026
· 7 phút đọc
19
Keep-alive HTTP
Tái dùng một kết nối cho 20 request nhanh gấp đôi trên đường RTT ~55 ms (2360 ms xuống 1195 ms) — nhưng trên localhost thì gần như vô hình. Đo thật.
03/09/2026
· 7 phút đọc
20
Chunked transfer
Với nội dung sinh dần, Content-Length buộc buffer hết (~530 ms mới có byte đầu) còn chunked stream từng khối (~105 ms) — nhanh gấp 5 lần. Đo thật.
03/09/2026
· 8 phút đọc
21
Nén HTTP
gzip giảm một JSON 20 KB còn 2,3 KB (88%); Brotli còn 1 KB (95%) — nhưng nén dữ liệu ngẫu nhiên hay tí xíu chỉ làm PHÌNH thêm. Đo thật gzip vs Brotli.
03/09/2026
· 8 phút đọc
22
Bộ nhớ đệm HTTP
Một revalidate với ETag khớp trả về 304 không kèm thân — 56 KB co xuống 152 byte. Nhưng ETag tính sai (đổi mỗi lần) biến 304 thành 200. Đo thật.
03/09/2026
· 7 phút đọc
23
Range request
Xin bytes=30000- để tiếp tục tải dở chỉ lấy về 70 KB còn lại thay vì cả 100 KB; xin một khúc giữa chỉ lấy 100 byte. Đo thật, kèm cái bẫy off-by-one.
03/09/2026
· 7 phút đọc
24
HTTP/2 ghép luồng
20 request qua HTTP/2 trên MỘT kết nối xong trong 249 ms; HTTP/1.1 sáu kết nối mất 823 ms, một kết nối nối đuôi mất 3396 ms. Đo thật, kèm một giả thuyết sai.
03/09/2026
· 7 phút đọc
25
HTTP/2 server push
Push né được một vòng khứ hồi cho khách mới, nhưng với khách quay lại đã cache CSS, nó gửi lại nguyên 33 KB để tiết kiệm vài ms. Đo cả lợi lẫn hại.
03/09/2026
· 7 phút đọc
26
HTTP/3 QUIC
Tôi định đo HTTP/3 bắt tay nhanh hơn và chịu mất gói tốt hơn HTTP/2 — nhưng phép đo cho kết quả ngược, vì QUIC thuần Python không thể đua với TCP của nhân.
03/09/2026
· 7 phút đọc
27
Cookie theo request
Một kho cookie 147 byte được kèm vào MỌI request tới miền — kể cả ảnh và CSS không dùng nó. Một trang 30 request tải lên ~4,4 KB chỉ riêng cookie. Đo thật.
03/09/2026
· 7 phút đọc
28
Chuyển hướng
Mỗi 301/302 thêm một vòng khứ hồi (~60 ms) khi dùng lại kết nối, nhưng ~2 vòng nếu đổi host. 301 được cache nên lần sau miễn phí; 302 thì không. Đo thật.
03/09/2026
· 7 phút đọc
29
Proxy ngược
Một lớp proxy ngược thêm bao nhiêu độ trễ? Cùng máy với backend thì gần như 0; khác máy thì thêm đúng một vòng khứ hồi giữa chúng. Đo thật bằng nginx.
03/09/2026
· 7 phút đọc
30
Cân bằng tải
Round-robin chia 60 request thành 20/20/20 — đều theo lượt. Nhưng least-connections chỉ đưa backend chậm 2 request, dồn phần còn lại cho hai backend rảnh. Đo thật.
03/09/2026
· 7 phút đọc
31
Kết nối gộp phía client
Kích thước bể kết nối là mức song song của client: pool 1 chạy 30 request mất 2581 ms, pool 30 chỉ 146 ms. Nhưng lợi ích dừng ở đúng sức backend. Đo thật.
03/09/2026
· 8 phút đọc
32
gRPC trên HTTP/2
Cùng một bản ghi, protobuf nhỏ hơn JSON ~2 lần và mã hóa nhanh hơn 7 lần — nhưng không phải 'nhỏ hơn chục lần' như đồn, và gzip còn làm phình payload nhỏ. Đo thật.
03/09/2026
· 8 phút đọc
33
Giữ một kết nối mở
Giữ 3000 WebSocket mở tốn 45,8 KB mỗi kết nối trên máy chủ Python — nhưng 3000 socket TCP trần chỉ tốn 0,1 KB. Cái đắt là runtime, không phải kết nối. Đo thật.
03/09/2026
· 10 phút đọc
34
Độ trễ hay băng thông
Một trang 40 tài nguyên nhỏ: qua 10 mbit là chững ~520ms, nhưng RTT kéo thời gian tăng tuyến tính. Một file 5MB thì ngược lại. Cái nào giới hạn tùy dạng tải. Đo thật.
03/09/2026
· 9 phút đọc
35
Trễ đuôi p99
Thêm 1% mất gói: p50 vẫn 28ms y như cũ, nhưng p99 nhảy từ 32ms lên 264ms. Mất gói không kéo trung vị — nó dựng một vách đứng ở cái đuôi. Đo thật bằng netem.
03/09/2026
· 8 phút đọc
36
Bóp băng thông
Bóp xuống 10 mbit thì tc làm chính xác. Nhưng màn bufferbloat kinh điển tôi định dựng lại không xảy ra: TCP tự ghìm, đệm 3MB chỉ đọng 8 gói. Chỉ flood UDP mới bơm đầy. Đo thật.
03/09/2026
· 8 phút đọc
37
Ánh xạ cổng NAT
NAT Linux không ném cho bạn cổng ngẫu nhiên: nó GIỮ cổng nguồn 40000 -> 40000, dùng cùng cổng ngoài cho mọi đích, chỉ đổi khi trùng. Đo thật bằng conntrack.
03/09/2026
· 8 phút đọc
38
Cô lập mạng
Hai network namespace cùng bind cổng 8080 mà không xung đột — nhưng bind lại trong cùng một vùng thì Errno 98. Danh tính socket là (vùng, địa chỉ, cổng), không phải số cổng. Đo thật.
03/09/2026
· 8 phút đọc
39
Cầu nối và veth
veth là sợi cáp ảo nối đúng hai vùng; cầu nối là switch ảo cho nhiều vùng. Tôi định chứng minh chuỗi cáp không phải mạng — nhưng nó chạy, vì netns thừa kế ip_forward=1 của Docker. Đo thật.
03/09/2026
· 8 phút đọc
40
Luật lọc gói
Một luật DROP làm client TREO 7 giây (mặc định ~127 giây) trong im lặng; REJECT từ chối ngay 0,1 ms. Và 10000 luật iptables làm RTT gấp 2,5 lần, còn một luật ipset thì không. Đo thật.
03/09/2026
· 8 phút đọc
41
Socket Unix và TCP
Cho hai tiến trình cùng máy, Unix socket nhanh hơn TCP loopback — nhưng chỉ ~1,6 lần độ trễ (18,8 so 30,2 us), không phải chục lần. Và bẫy Nagle tôi giăng sẵn không sập. Đo thật.
03/09/2026
· 7 phút đọc
42
Một luồng TCP
Bắt gói một lần GET: 10 gói, mà chỉ 2 gói chở HTTP — 8 gói còn lại là bắt tay, ACK và đóng kết nối. Và một byte ma của SYN/FIN suýt làm tôi đọc sai bản bắt. Đo thật bằng tcpdump.
03/09/2026
· 7 phút đọc
43
Vòng đời kết nối
Đọc vòng đời một kết nối TCP qua ss: LISTEN, ESTABLISHED, TIME-WAIT, CLOSE-WAIT. Hạ tcp_fin_timeout không rút được TIME-WAIT — nó chỉnh một trạng thái khác. Đo thật.
03/09/2026
· 7 phút đọc
44
Thoái lui và jitter
Thử lại ngay dội 2000 request vào một server chỉ lỗi 2 giây. Và exponential backoff KHÔNG jitter vẫn khiến 50 client thử lại thành từng đợt ~50 gói đồng bộ — jitter mới dập được. Đo thật.
03/09/2026
· 8 phút đọc
45
Ngân sách độ trễ
Bốn chặng gọi tuần tự tốn 261ms; song song hoá còn 63ms. Một chặng chậm +100ms đẩy tổng song song lên 172ms — bằng chặng chậm nhất, không phải tổng. Bài cuối của sê-ri. Đo thật.
03/09/2026
· 7 phút đọc