AIHub

网络基础抓包级理解:HTTP、TCP/IP 与 DNS

入门20 分钟读完2026-08-06#后端#网络#HTTP#TCP
网络基础抓包级理解:HTTP、TCP/IP 与 DNS

排查线上问题时,新手和后端专家的分水岭往往出现在第一句话。接口慢了,新手问“是不是我代码写得不好”,专家问“是 TCP 建连慢、TLS 握手慢、服务端处理慢,还是网络传输慢”——因为一次 HTTP 请求在你代码执行之前,已经走过了 DNS、TCP、TLS 三关

本篇不背八股文,而是用真实命令让你亲眼看到这一切。学完你能做两件大多数人做不到的事:用 curl 拆解请求的每个耗时阶段,用抓包工具看懂 TCP 握手。

一次请求的全景旅程

当你在浏览器输入 https://api.example.com/users,发生了什么?

  1. DNS 解析:把域名翻译成 IP 地址,类似查电话簿——你知道名字,但需要号码才能拨号。
  2. TCP 建连:与目标服务器进行三次握手,建立一条可靠的双向通道。
  3. TLS 握手(HTTPS):在 TCP 通道之上协商加密密钥,之后的所有内容都加密传输。
  4. 发送 HTTP 请求:你的请求报文终于出发了。
  5. 服务器处理并返回:这才是你写的代码干活的部分。

动手验证一下,用 curl 的计时参数拆解每个阶段:

curl -o /dev/null -s -w \
  "DNS 解析:    %{time_namelookup}s\n\
TCP 建连:   %{time_connect}s\n\
TLS 握手:   %{time_appconnect}s\n\
首字节到达: %{time_starttransfer}s\n\
总耗时:     %{time_total}s\n" \
  https://www.baidu.com

输出大致是:

DNS 解析:    0.012s
TCP 建连:   0.045s
TLS 握手:   0.098s
首字节到达: 0.180s
总耗时:     0.182s

注意看:首字节到达(0.180s)减去 TLS 握手完成(0.098s),中间的 82ms 才是服务器真正处理的时间。如果哪天接口变慢,先看慢在哪个阶段——慢在 connect 是网络问题,慢在 starttransfer 才是你代码的问题。这就是“抓包级理解”带来的判断力。

一次 HTTP 请求的分层旅程

DNS:互联网的通讯录

域名系统(DNS)做的事听起来简单:把 api.example.com 翻译成 IP 地址。但它是几乎所有请求的必经之路,也是排查“打不开”类问题的第一站。

一次 DNS 查询的完整链路是:浏览器缓存 → 操作系统缓存 → 本地 hosts 文件 → 递归解析器(通常是运营商或公共 DNS)→ 根域名服务器 → 顶级域服务器(.com)→ 权威域名服务器,拿到结果后逐级缓存返回。每一级缓存都有一个有效期(TTL),TTL 到期前大家都用缓存答案——这就是为什么你改了域名解析,要过一会儿才全球生效。

dig 可以亲眼看到解析结果:

dig www.baidu.com

重点看输出里的 ANSWER SECTION:每个回答都是一条记录,常见类型有四种——A 记录(域名指向 IPv4 地址)、AAAA 记录(指向 IPv6)、CNAME 记录(域名指向另一个域名,CDN 全靠它)、MX 记录(邮件服务器)。想看完整查询链路可以加 +trace 参数。

一个真实案例:用户反馈“你们服务挂了”,但你这边监控一切正常——最后发现是某地区运营商的 DNS 缓存了一条过期记录。服务不可用时,先用 dig 确认域名解析是否正确,能帮你省掉无数次冤枉的代码排查。

TCP 三次握手与四次挥手

TCP 是可靠传输协议:它保证数据不丢、不乱序。代价是传数据之前必须先“打电话确认对方在听”。

三次握手的对话是这样的:

  1. 客户端:SYN ——“喂,听得到吗?”
  2. 服务端:SYN + ACK ——“听得到,你听得到我吗?”
  3. 客户端:ACK ——“我也听得到,开始说正事。”

为什么不是两次? 因为两次只能证明“客户端能发、服务端能收”,服务端无法确认自己的声音客户端是否听得到。第三次握手让双方都确认了收发能力。为什么不是四次?因为第二次把 SYN 和 ACK 合并了,没必要多一次。

四次挥手(断开连接)则是:

  1. 主动方:FIN ——“我说完了。”
  2. 被动方:ACK ——“知道了,但我还有话没说完。”
  3. 被动方:FIN ——“我也说完了。”
  4. 主动方:ACK ——“好,挂吧。”

为什么挥手比握手多一次?因为断开时被动方可能还有数据没发完,ACK 和 FIN 不能合并。主动方最后会进入 TIME_WAIT 状态等待一段时间(通常 2 个报文最大生存时间),确保最后一个 ACK 被对方收到。高并发短连接服务里看到大量 TIME_WAIT,根源就在这里——这也是面试常考、线上常见的真实问题。

在 macOS 或 Linux 上,你可以用 tcpdump 亲眼看到握手过程:

# 终端 1:抓 443 端口的包
sudo tcpdump -i any -n 'tcp port 443 and tcp[tcpflags] & (tcp-syn|tcp-ack|tcp-fin) != 0'

# 终端 2:发起一个 HTTPS 请求
curl https://www.baidu.com

你会在终端 1 看到 [S](SYN)、[S.](SYN+ACK)、[.](ACK)依次出现——那就是三次握手本尊。图形化工具推荐 Wireshark,筛选 tcp.flags.syn==1 效果更直观。

HTTP 方法与状态码:后端的通用语言

HTTP 方法不是随便选的,它有语义,RESTful API 设计完全建立在这套语义上:

  • GET:获取资源,幂等且不应有副作用(调一次和调一百次结果一样)
  • POST:创建资源或触发动作,非幂等(重复提交会产生重复订单)
  • PUT:整体替换资源,幂等
  • PATCH:局部更新资源
  • DELETE:删除资源,幂等

状态码按首位数字分五大类,每类记住代表选手就够:

类别 含义 必记代表
2xx 成功 200 OK、201 Created、204 No Content
3xx 重定向 301 永久、302 临时、304 缓存未修改
4xx 客户端的错 400 参数错、401 未登录、403 没权限、404 不存在、429 请求太频繁
5xx 服务端的错 500 内部错误、502 网关上游挂了、503 服务不可用、504 网关超时

一个排障速查直觉:4xx 找调用方,5xx 找服务方。收到 502 别去翻自己的业务代码,那是网关告诉你上游服务没响应——先去看上游进程还活着没。

curl -v 可以看到完整的请求和响应报文,这是理解 HTTP 最好的方式,没有之一:

curl -v https://httpbin.org/status/418

> 开头的是你发出去的请求行和请求头,< 开头的是服务器返回的状态行和响应头。花十分钟把这个输出逐行看懂,胜过背十篇 HTTP 教程。

HTTPS:在窃听者眼皮底下安全通信

HTTP 是明文传输,公共 Wi-Fi 下任何人都能看到你的请求内容。HTTPS = HTTP + TLS,核心问题是:怎么在不安全的网络上,安全地协商出一个只有通信双方知道的密钥?

答案是混合加密,简化版的 TLS 握手流程:

  1. 客户端:我支持这些加密套件,这是我的随机数 A
  2. 服务端:选这个套件,这是我的随机数 B,以及我的证书(内含公钥)
  3. 客户端验证证书(是不是受信任机构签发、域名对不对、过没过期),然后生成“预备主密钥”,用服务端公钥非对称加密后发给服务端
  4. 双方用随机数 A + B + 预备主密钥,各自算出同一个对称会话密钥
  5. 之后所有数据用这个对称密钥加密传输

为什么不全程用非对称加密?因为非对称加密比对称加密慢几个数量级,只用它传一次密钥,之后全走快的对称加密——用非对称解决信任,用对称解决效率,这是工程上的经典权衡。

如果看到 curl: (60) SSL certificate problem,可以用 -k 跳过验证调试(仅限本地调试,生产代码里永远不要这么做):

curl -kv https://self-signed.badssl.com/

长连接与 HTTP/2

HTTP/1.0 时代,每个请求都要重新建一次 TCP 连接——三次握手加 TLS 握手,光建连就要上百毫秒。于是有了两次重要进化。

HTTP/1.1 长连接(Keep-Alive,默认开启):一次 TCP 连接可以连续发多个请求,省掉重复握手。但同一时刻一个连接上只能处理一个请求,前一个没回来后面就得排队,这叫队头阻塞

HTTP/2 多路复用:把请求拆成带编号的帧,多个请求的帧在同一条 TCP 连接上交错传输、互不等待。6 个静态资源请求,HTTP/1.1 要么排队要么开 6 条连接,HTTP/2 一条连接全搞定。

验证一个网站是否支持 HTTP/2:

curl -sI --http2 https://www.cloudflare.com | head -1
# 输出: HTTP/2 200

注意 HTTP/2 解决的是 HTTP 层的队头阻塞,TCP 层的队头阻塞依然存在(一个 TCP 包丢了,所有流都得等它重传),这正是 HTTP/3 改用基于 UDP 的 QUIC 协议的原因——知道有这回事即可。

对后端工程师来说,这些演进有一个非常实际的推论:连接是昂贵的,复用是王道。这就是为什么连接数据库要用连接池、调下游服务要开启 Keep-Alive——每省掉一次握手,就省掉一个网络往返。很多“接口偶尔慢 100ms”的玄学问题,最后查出来都是客户端每次请求都新建连接。

常见误区

  • 误区一:“HTTPS 慢,内网不用上”。 TLS 握手开销在长连接下会被摊薄,且内网明文传输等于把数据交给所有能接触到流量的人。
  • 误区二:“GET 和 POST 没区别,反正都能传参。” 区别在语义和约定:GET 会被缓存、被代理记录、被浏览器预取,把写操作放在 GET 上是事故温床(曾有网站被浏览器预取功能删光了数据)。
  • 误区三:“三次握手是面试题,工作用不到。” 直到你遇到 TIME_WAIT 堆积、SYN Flood 攻击、连接池打满——它们全是握手和挥手的直接后果。
  • 误区四:凭感觉排查网络问题。 永远先用 curl -w 拆阶段、用 tcpdump/Wireshark 看包,数据会告诉你答案。

小结

一次 HTTP 请求的旅程是:DNS 查号 → TCP 三次握手建通道 → TLS 协商密钥 → 发送报文。TCP 握手三次是因为双方都要确认收发能力,挥手四次是因为被动方可能有数据没发完。HTTP 方法和状态码是后端世界的通用语言,4xx 找调用方、5xx 找服务方。HTTPS 用非对称加密换信任、对称加密换效率。HTTP/2 用多路复用解决了 HTTP 层队头阻塞。

最重要的是方法论:网络问题不要用猜的,用 curl -vcurl -w 和抓包工具去看。下一篇我们进入后端工程师真正的主战场——Linux 命令行。


系列导航:上一篇《从 0 到后端专家:全景路线图与学习方法论》(/content/backend-expert-01-roadmap) · 下一篇《Linux 与命令行:后端工程师的日常武器》(/content/backend-expert-03-linux)

相关教程

网络基础抓包级理解:HTTP、TCP/IP 与 DNS | AIHub