TCP, HTTP & Web cho Backend/Bộ công cụ debug mạng — dig, curl, nc, ss, tcpdump
25/29
Bài 25 / 29~22 phútTLS & Công cụ DebugMiễn phí lượt xem

Bộ công cụ debug mạng — dig, curl, nc, ss, tcpdump

Năm công cụ dòng lệnh, mỗi cái soi đúng một chặng: dig cho DNS, curl cho HTTP/TLS/timing, nc cho TCP thô, ss cho socket local, tcpdump cho gói thật. Workflow chẩn đoán theo chặng để biết hỏng ở đâu.

TL;DR: Khi một request "không chạy", lỗi có thể nằm ở bất kỳ chặng nào: phân giải DNS, bắt tay TCP, handshake TLS, hay tầng HTTP. Mỗi công cụ soi đúng một chặng — dig kiểm tra DNS resolution, curl -v -w cho thấy timing từng pha của một request HTTP/TLS, nc test một port TCP có mở không (tách biệt khỏi tầng ứng dụng), ss/lsof liệt kê socket và process local, tcpdump bắt gói thật trên dây khi mọi thứ khác không đủ. Bí quyết không phải nhớ mọi flag, mà là chẩn đoán theo chặng: đi từ ngoài (DNS) vào trong (HTTP) cho tới khi tìm ra chặng đầu tiên hỏng. Bài này gắn mỗi công cụ với chặng nó soi và đưa ra workflow chẩn đoán thực dụng.

Bạn đã hiểu từng tầng — DNS phân giải tên (Foundations), TCP bắt tay (module 1 bài 01), TLS handshake (bài 01), HTTP request/response (module 2). Bài này cho bạn công cụ nhìn vào từng tầng đó khi nó hỏng — kỹ năng phân biệt giữa "DNS sai", "port đóng", "cert hết hạn" và "app trả 500".

1. Analogy — Bộ đồ nghề thợ sửa nước

Một thợ sửa nước giỏi không có một cờ-lê vạn năng — họ có một bộ đồ nghề, mỗi món cho một việc. Khi nước không chảy, họ kiểm tra theo chặng: van tổng có mở không, ống có tắc không, vòi có hỏng không.

Debug mạng giống hệt. Khi request không tới đích, bạn kiểm tra từng chặng bằng đúng công cụ cho chặng đó — thay vì đoán mò.

Đồ nghề thợ nướcCông cụ mạngSoi chặng nào
Tra sổ địa chỉ nhàdigDNS — tên ra IP đúng không
Mở van thử nướcncTCP — port có mở, có accept không
Bơm nước qua toàn hệ thống, đo ápcurl -v -wHTTP + TLS — cả luồng end-to-end + timing
Kiểm tra đồng hồ nước trong nhàss / lsofSocket local — process nào nghe port nào
Đặt camera trong ống xem dòng chảytcpdumpGói thật trên dây — khi mọi thứ khác chưa rõ
💡 Cách nhớ

Chọn công cụ theo chặng nghi ngờ, không theo thói quen. Tên không phân giải → dig. Port không mở → nc. Request chậm/lỗi → curl -v. Process local → ss. Không rõ gói có ra/vào không → tcpdump. Đi từ ngoài vào trong.

Một request đi qua các chặng theo thứ tự — mỗi chặng có công cụ soi riêng. Debug là đi dọc chuỗi này từ ngoài vào trong, dừng ở chặng đầu tiên hỏng:

DNS
tên → IP
dig
TCP
port mở?
nc
TLS + HTTP
cert, header, timing
curl -v -w
Gói thật
khi chưa rõ
tcpdump

ss / lsof soi phía local (process nào nghe port nào) — cắt ngang mọi chặng

2. dig — soi tầng DNS

dig (Domain Information Groper) hỏi trực tiếp DNS server và in ra câu trả lời thô — chính xác hơn ping vì tách riêng bước phân giải tên khỏi mọi thứ khác.

# Hoi A record (ten -> IPv4)
dig example.com

# Chi in dap an, gon
dig +short example.com

# Hoi mot resolver cu the (Google DNS) - bo qua cache he thong
dig @8.8.8.8 example.com

# Truy vet toan bo chain tu root server
dig +trace example.com

# Hoi loai record khac
dig example.com MX        # mail server
dig example.com NS        # name server
dig example.com AAAA      # IPv6

Phần quan trọng nhất trong output là ANSWER SECTION và cột TTL:

;; ANSWER SECTION:
example.com.    3000    IN    A    93.184.216.34

Cột 3000 là TTL còn lại (giây) — cho biết bản ghi này còn được cache bao lâu. Khi đổi DNS mà thay đổi "chưa có tác dụng", thủ phạm thường là TTL cũ chưa hết. So sánh kết quả dig với dig @8.8.8.8 (resolver ngoài) để phân biệt "DNS thật sự sai" và "cache local cũ".

⚠️ ping không thay được dig

ping example.com cũng phân giải DNS, nhưng nếu fail bạn không biết DNS sai hay ICMP bị chặn. dig chỉ làm một việc — phân giải tên — nên kết quả không nhập nhằng. Luôn dùng dig khi nghi ngờ DNS.

3. curl — soi tầng HTTP và TLS end-to-end

curl là con dao đa năng nhất. Hai flag biến nó thành công cụ debug: -v (verbose — in từng bước) và -w (write-out — đo timing).

# -v: in chi tiet ket noi, TLS handshake, header gui/nhan
curl -v https://example.com

# Chi xem response header, khong tai body (HEAD request)
curl -I https://example.com

# Theo redirect (-L) va in header moi hop
curl -ILv https://example.com

Output -v đánh dấu hướng bằng ký hiệu:

  • * — ghi chú của curl (DNS, kết nối, TLS).
  • > — dữ liệu gửi đi (request header).
  • < — dữ liệu nhận về (response header).
* Connected to example.com (93.184.216.34) port 443
* TLS 1.3 handshake, Cipher: TLS_AES_256_GCM_SHA384
* Server certificate: subject: CN=example.com; expire date: Jan 15 2027
> GET / HTTP/2
> Host: example.com
< HTTP/2 200
< content-type: text/html

Dòng Server certificate ... expire date cho biết ngay cert có hết hạn không — một trong những lỗi production phổ biến nhất.

Đo timing từng pha với -w — đây là kỹ thuật vàng để tìm chặng nào chậm:

curl -w "dns: %{time_namelookup}s  connect: %{time_connect}s  tls: %{time_appconnect}s  ttfb: %{time_starttransfer}s  total: %{time_total}s\n" \
     -o /dev/null -s https://example.com
dns: 0.004s  connect: 0.028s  tls: 0.089s  ttfb: 0.142s  total: 0.145s

Đọc các mốc tích luỹ này để khoanh vùng:

  • time_namelookup cao → DNS chậm.
  • time_connect trừ time_namelookup cao → TCP handshake chậm (mạng xa, mất gói).
  • time_appconnect trừ time_connect cao → TLS handshake chậm.
  • time_starttransfer trừ time_appconnect cao → server xử lý chậm (TTFB — time to first byte).

4. nc — soi tầng TCP thô

nc (netcat) mở một kết nối TCP/UDP trần, tách biệt hoàn toàn khỏi HTTP/TLS. Dùng để trả lời đúng một câu: port này có mở và accept kết nối không?

# Test port co mo khong (-z: scan, -v: verbose)
nc -zv example.com 443
# Connection to example.com port 443 [tcp/https] succeeded!

# Test nhieu port
nc -zv example.com 80 443 8080

# Mo ket noi tho va go HTTP request bang tay
nc example.com 80
GET / HTTP/1.1
Host: example.com

# (go dong trong roi Enter de gui)

nc -zv tách lỗi rất gọn: nếu succeeded, vấn đề nằm ở tầng trên TCP (app, TLS, HTTP). Nếu Connection refused, không có process listen (xem module 1 bài 07 — lỗi mạng về ECONNREFUSED). Nếu timeout, firewall đang DROP gói âm thầm.

📌 nc phân biệt 3 trạng thái port

succeeded: có process listen, TCP bắt tay xong → tầng dưới ổn, soi tiếp tầng trên. refused (tức thì): gói tới được host nhưng không ai nghe port đó → sai port hoặc app chưa chạy. timeout (chậm, im lặng): firewall chặn bằng DROP, hoặc host không reachable → vấn đề định tuyến/firewall.

5. ss và lsof — soi socket và process local

ss (socket statistics — thay thế netstat cũ) liệt kê socket trên chính máy bạn. Trả lời: process nào đang nghe port nào, có bao nhiêu kết nối, ở state gì.

# Liet ke moi socket TCP dang LISTEN + process (-t TCP, -l listen, -n so, -p process)
ss -tlnp

# Moi ket noi TCP kem state
ss -tan

# Loc theo port
ss -tlnp 'sport = :8080'

# Dem ket noi theo state (chan doan TIME_WAIT/CLOSE_WAIT)
ss -tan state time-wait | wc -l
ss -tan state close-wait
State    Recv-Q  Send-Q  Local Address:Port   Peer Address:Port  Process
LISTEN   0       128     0.0.0.0:8080         0.0.0.0:*          users:(("java",pid=4211))

lsof (list open files — socket cũng là file trên Unix) trả lời ngược lại: port này đang bị process nào giữ? — hữu ích khi gặp EADDRINUSE:

# Process nao dang giu port 8080?
lsof -i :8080

# Moi ket noi mang cua mot process
lsof -i -a -p 4211

Hai công cụ này soi phía local — khác hẳn dig/curl/nc soi phía remote. Khi app báo "không bind được port" hay "quá nhiều connection", đây là nơi nhìn đầu tiên. State machine TCP (LISTEN, ESTABLISHED, TIME_WAIT, CLOSE_WAIT) đã được mổ ở module 1 bài 04.

6. tcpdump — soi gói thật trên dây

Khi mọi công cụ trên vẫn chưa cho câu trả lời, tcpdump bắt gói thật đi qua interface — sự thật cuối cùng. Nó trả lời: "gói có thực sự ra/vào không, và nội dung header là gì?"

# Bat goi tren interface eth0, port 443
sudo tcpdump -i eth0 'tcp port 443'

# Bat goi toi/tu mot host cu the, in chi tiet (-v), khong phan giai ten (-n)
sudo tcpdump -i eth0 -n -v host 93.184.216.34

# Chi bat goi SYN (tim ket noi moi) - flag bit
sudo tcpdump -i eth0 'tcp[tcpflags] & tcp-syn != 0'

# Luu ra file .pcap de mo trong Wireshark
sudo tcpdump -i eth0 -w capture.pcap 'tcp port 443'

Cú pháp filter là BPF (Berkeley Packet Filter) — giống capture filter của Wireshark. Output một dòng mỗi gói:

10:24:01.123456 IP 10.0.0.5.52341 > 93.184.216.34.443: Flags [S], seq 0, win 65535
10:24:01.135678 IP 93.184.216.34.443 > 10.0.0.5.52341: Flags [S.], seq 0, ack 1
10:24:01.135700 IP 10.0.0.5.52341 > 93.184.216.34.443: Flags [.], ack 1

Bạn thấy ngay 3-way handshake: [S] (SYN), [S.] (SYN-ACK), [.] (ACK). Nếu chỉ thấy [S] lặp lại mà không có [S.] trả về → gói SYN ra được nhưng không có phản hồi → firewall chặn hoặc server không listen. tcpdump là CLI tương đương Wireshark — workflow thực tế là tcpdump -w trên server rồi mở file trong Wireshark (bài 04) để phân tích GUI.

⚠️ tcpdump cần quyền root và filter chặt

tcpdump cần sudo (đọc raw socket). Trên server production nhiều traffic, luôn đặt filter chặt (host X and port Y) — bắt không filter sinh hàng triệu gói/phút, lấp đầy đĩa. Với HTTPS bạn chỉ thấy header TCP và ciphertext TLS — không đọc được nội dung HTTP (cần key log như bài 04 mô tả).

7. Workflow chẩn đoán theo chặng

Khi "trang không load", đừng đoán — đi tuần tự từ ngoài vào trong, dừng ở chặng đầu tiên hỏng:

flowchart TB
    A["Trang khong load"] --> B{"dig: ten ra IP dung?"}
    B -->|"Sai/khong co"| B1["Loi DNS: sai record, TTL cu, resolver hong"]
    B -->|"Dung"| C{"nc -zv host port: port mo?"}
    C -->|"refused"| C1["App chua chay / sai port (ss -tlnp tren server)"]
    C -->|"timeout"| C2["Firewall DROP / route hong (tcpdump xem SYN co phan hoi)"]
    C -->|"succeeded"| D{"curl -v: TLS + HTTP ok?"}
    D -->|"cert loi"| D1["Cert het han / sai CN / thieu chain (bai 02)"]
    D -->|"HTTP 5xx"| D2["App loi - doc log server, khong phai loi mang"]
    D -->|"cham"| D3["curl -w: pha nao cham? TTFB cao -> app cham"]
    D -->|"200 OK"| E["Mang OK - van de o tang ung dung/browser"]

Ví dụ thực chiến — "API trả lỗi gián đoạn":

# 1. DNS dung khong?
dig +short api.example.com
# -> 93.184.216.34  (ok)

# 2. Port mo khong?
nc -zv api.example.com 443
# -> succeeded  (TCP ok, khong phai loi mang co ban)

# 3. TLS + HTTP + timing
curl -w "tls: %{time_appconnect}s  ttfb: %{time_starttransfer}s\n" \
     -o /dev/null -s https://api.example.com/health
# -> tls: 0.09s  ttfb: 3.20s   (!! TTFB 3.2s -> server xu ly cham)

# 4. Ket luan: khong phai loi mang. Doc log app, kiem tra DB/downstream.

Giá trị của workflow: nó phân biệt lỗi mạng với lỗi ứng dụng. Phần lớn "lỗi mạng" hoá ra là app chậm, cert hết hạn, hoặc sai port — không phải mạng thật sự hỏng.

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

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

📚 Man page và tài liệu công cụ

Tra cứu chính thức:

Ghi chú: man curl phần WRITE-OUT là tài liệu đáng đọc nhất khi muốn đo performance — danh sách time_namelookup, time_connect, time_appconnect, time_starttransfer, time_total cho phép dựng một script profiling chỉ bằng curl.

10. Tóm tắt

  • Mỗi công cụ soi đúng một chặng — chọn theo chặng nghi ngờ, đi từ ngoài (DNS) vào trong (HTTP).
  • dig kiểm tra DNS resolution tách biệt khỏi mọi thứ khác; đọc ANSWER SECTION và TTL; @8.8.8.8 bỏ qua cache local.
  • curl -v cho thấy TLS handshake, cert expiry, header gửi/nhận (* ghi chú, > gửi, < nhận); curl -w đo timing từng pha để khoanh vùng chặng chậm (TTFB cao = app chậm).
  • nc -zv test một port TCP: succeeded / refused / timeout — ba trạng thái chỉ về ba loại lỗi khác nhau.
  • ss/lsof soi phía local: process nào nghe port nào, state TCP, ai giữ port khi EADDRINUSE.
  • tcpdump bắt gói thật khi cần sự thật cuối cùng; filter BPF chặt, cần root; với HTTPS chỉ thấy header + ciphertext.
  • Workflow chẩn đoán: dignccurl -v/-wtcpdump, dừng ở chặng đầu tiên hỏng — phần lớn "lỗi mạng" thật ra là app chậm/cert hết hạn/sai port.

11. Tự kiểm tra

Tự kiểm tra
Q1
Vì sao dùng dig tốt hơn ping khi nghi ngờ vấn đề DNS?
ping làm hai việc trộn lẫn: phân giải DNS rồi gửi ICMP echo. Nếu ping fail, bạn không biết DNS sai hay ICMP bị firewall chặn — kết quả nhập nhằng. dig chỉ làm một việc: hỏi DNS server và in câu trả lời thô (record, TTL, resolver nào trả lời). Nó cũng cho phép hỏi một resolver cụ thể (dig @8.8.8.8) để phân biệt "DNS thật sự sai" với "cache local cũ", và xem TTL còn lại để biết vì sao thay đổi DNS chưa có tác dụng. Khi nghi DNS, luôn dùng dig vì kết quả không lẫn với tầng khác.
Q2
curl -w cho time_namelookup=0.005s, time_connect=0.03s, time_appconnect=0.09s, time_starttransfer=2.5s. Chặng nào là nút thắt? Vì sao?
Các mốc này tích luỹ từ đầu request. Tính delta từng pha: DNS = 0.005s (nhanh), TCP connect = 0.03 − 0.005 = 0.025s (nhanh), TLS = 0.09 − 0.03 = 0.06s (bình thường), nhưng TTFB = time_starttransfer − time_appconnect = 2.5 − 0.09 = 2.41s (rất chậm). Nút thắt là thời gian server xử lý (time to first byte) — không phải mạng. DNS, TCP, TLS đều nhanh; toàn bộ độ trễ nằm ở khoảng từ lúc TLS xong tới lúc byte đầu của response về. Hướng debug: đọc log server, kiểm tra query DB chậm hay downstream service — đây không phải vấn đề mạng.
Q3
nc -zv host 443 trả về ba kết quả khác nhau: succeeded, Connection refused, timeout. Mỗi cái chỉ về loại lỗi gì?
succeeded: gói tới được host, có process listen port 443, TCP bắt tay xong → tầng TCP trở xuống ổn, lỗi (nếu có) nằm ở tầng trên (TLS, HTTP, app) — soi tiếp bằng curl -v. Connection refused (tức thì): gói tới được host nhưng không ai nghe port đó, OS trả RST ngay → app chưa chạy hoặc sai port (kiểm tra ss -tlnp trên server). timeout (chậm, im lặng): không có phản hồi gì cả → firewall đang DROP gói âm thầm, hoặc host không reachable (route hỏng). Ba kết quả tách lỗi gọn gàng theo ba hướng debug hoàn toàn khác nhau.
Q4
ss/lsof khác dig/curl/nc ở điểm cốt lõi nào về hướng soi? Khi nào bạn cần ss thay vì curl?
dig, curl, nc soi phía remote — chúng gửi request ra ngoài và xem đích trả lời thế nào. sslsof soi phía local — liệt kê socket và process trên chính máy bạn, không gửi gì ra mạng. Cần ss thay vì curl khi câu hỏi là về máy local: "app của tôi có thực sự bind port 8080 không?" (ss -tlnp), "có bao nhiêu connection đang ESTABLISHED / TIME_WAIT?", "process nào đang giữ port khiến tôi gặp EADDRINUSE?" (lsof -i :8080). curl không trả lời được những câu này vì nó nhìn từ ngoài vào, còn ss nhìn từ trong ra.
Q5
Trong output tcpdump bạn chỉ thấy các gói Flags [S] gửi đi lặp lại mà không có [S.] trả về. Điều này nói lên gì?
[S] là gói SYN — bước đầu của 3-way handshake; [S.] là SYN-ACK (server trả lời). Thấy SYN gửi đi lặp lại nhưng không có SYN-ACK về nghĩa là: gói SYN của bạn ra được khỏi máy (tcpdump bắt được trên interface), nhưng phía bên kia không phản hồi. Hai nguyên nhân chính: (1) firewall đang DROP gói (không reject, chỉ nuốt im lặng) — đây cũng là lý do bạn thấy ETIMEDOUT chứ không phải ECONNREFUSED; (2) server không listen port đó và route khiến gói không tới được, hoặc SYN-ACK trả về bị chặn ở chiều ngược. SYN lặp lại là TCP tự retransmit khi không nhận ACK. Bước tiếp: kiểm tra firewall/security group và xác nhận server có listen (ss -tlnp trên server).
Q6
Vì sao workflow chẩn đoán đi theo thứ tự dig → nc → curl → tcpdump, và lợi ích của việc dừng ở chặng đầu tiên hỏng là gì?
Thứ tự này đi từ chặng ngoài cùng vào trong, đúng theo trình tự một request thật phải vượt qua: phân giải tên (DNS) → mở kết nối (TCP) → bắt tay bảo mật và trao đổi dữ liệu (TLS + HTTP) → và tcpdump là công cụ "sự thật cuối cùng" khi các tầng trên chưa rõ. Nếu DNS đã sai thì không cần test port; nếu port đã đóng thì không cần test TLS. Dừng ở chặng đầu tiên hỏng tiết kiệm thời gian (không debug nhầm tầng) và quan trọng hơn là phân biệt được lỗi mạng với lỗi ứng dụng — phần lớn "trang không load" hoá ra là app trả 5xx, cert hết hạn, hoặc sai port, chứ không phải mạng thật sự hỏng. Đoán mò thường sửa nhầm chỗ.

Bài tiếp theo: Wireshark — bắt và đọc gói tin từng tầng

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

Wireshark — bắt và đọc gói tin từng tầng