Lập trình 22/09/2026 6 phút

TCP bắt tay 3 bước nhìn từ code: vì sao mỗi kết nối mới tốn một vòng khứ hồi

Mỗi lần net.Dial hay mở một kết nối HTTP, TCP phải bắt tay 3 bước SYN/SYN-ACK/ACK trước khi gửi được byte dữ liệu đầu tiên — tốn đúng một RTT. Bài này chạy thật trong Go: đo thời gian bắt tay, so kết nối mới mỗi request với tái dùng một kết nối (nhanh hơn 13 lần), và đọc trạng thái socket từ /proc/net/tcp để thấy LISTEN, ESTABLISHED và đống TIME_WAIT.

Lập trình 22/09/2026 5 phút

Socket buffer và listen backlog: hai hàng đợi ẩn quyết định TCP nghẽn ở đâu

Đằng sau mỗi socket TCP là hai hàng đợi trong kernel ít người để ý: buffer nhận/gửi và hàng đợi backlog. Buffer đầy thì TCP chặn người gửi (flow control, khác UDP mất gói); backlog đầy thì kết nối mới bị rớt lúc tải đỉnh. Bài này chạy thật trong Go: client bị chặn sau 44KB với buffer nhỏ, và mở được đúng 4097 kết nối trước khi backlog đầy (somaxconn=4096).

Lập trình 22/09/2026 6 phút

Trạng thái socket TCP: đọc /proc/net/tcp để phân biệt TIME_WAIT bình thường với CLOSE_WAIT là bug

Một kết nối TCP đi qua nhiều trạng thái, và hai trong số đó hay gây sự cố: TIME_WAIT và CLOSE_WAIT. Bài này chạy thật trong Go đọc /proc/net/tcp: mở 3000 kết nối ngắn để lại 3091 TIME_WAIT (ăn cổng cục bộ), còn server quên gọi Close để lại đúng 200 CLOSE_WAIT (rò rỉ fd). Giải thích vì sao TIME_WAIT là bình thường còn CLOSE_WAIT tăng không giảm luôn là bug.