2. 网络协议回顾:TCP/IP协议栈、TCP三次握手与四次挥手、HTTP/HTTPS协议基础

做高并发抓包,说白了就是跟网络协议打交道。你连协议都不熟,抓回来的包就是一堆乱码。今天咱们把TCP/IP协议栈、三次握手、四次挥手,还有HTTP/HTTPS这些基础再过一遍。嗯,都是老生常谈的东西,但每次讲抓包课,我都要强调一遍——基础不牢,地动山摇。

2.1 TCP/IP协议栈:分层模型与核心职责

TCP/IP协议栈,我习惯把它理解成「快递分拣系统」。每一层只干自己的活,上层只管把包裹扔给下层,下层只管把包裹送到下一站。这种分层设计,让网络协议变得异常灵活。

咱们直接看这张图,一目了然:

TCP/IP 四层协议栈模型 应用层 HTTP / HTTPS / FTP / DNS / SMTP 传输层 TCP(可靠) / UDP(不可靠) 网络层 IP(路由与寻址) / ICMP / ARP 网络接口层 以太网 / WiFi / 物理介质 数据 段 包 帧

每一层封装时都会加上自己的头部信息。抓包时你看到的那些乱七八糟的字节,其实就是一层层头部堆叠的结果。我个人习惯在排查问题时,先从应用层往下捋,看是哪一层出了问题。

核心要点:抓包时,你看到的每一个数据包,都包含了从网络接口层到应用层的完整头部信息。理解分层,才能看懂包结构。

2.2 TCP三次握手:建立连接的「双向确认」

TCP是面向连接的协议。什么叫「面向连接」?就是通信前,双方得先打个招呼,确认对方在线、愿意聊天、能收能发。这个打招呼的过程,就是三次握手。

我曾经在排查一个高并发连接超时问题时,发现服务端半连接队列满了,导致大量握手失败。那次之后,我对三次握手的理解就再也不是书本上的死知识了。

咱们用表格把三次握手的过程拆开看:

步骤 发送方 → 接收方 报文标志位 核心含义
第一次握手 客户端 → 服务端 SYN=1, seq=x 「嘿,我想跟你建立连接,我的初始序号是x」
第二次握手 服务端 → 客户端 SYN=1, ACK=1, seq=y, ack=x+1 「收到!我同意,我的初始序号是y,我期待你下一个包序号是x+1」
第三次握手 客户端 → 服务端 ACK=1, seq=x+1, ack=y+1 「好的,我知道了,咱们开始传数据吧」

你想想看,为什么非得三次?两次不行吗?

我简单解释一下:如果只有两次握手,服务端无法确认客户端是否收到了自己的SYN+ACK。万一这个包丢了,客户端以为自己没连上,服务端却以为连上了,就会白白浪费资源等待数据。三次握手,本质上是「双方各自确认对方的接收能力正常」。

抓包小技巧:用Wireshark过滤三次握手,直接输入 tcp.flags.syn == 1。你会看到三个包:第一个只有SYN,第二个SYN+ACK,第三个只有ACK。少一个都不行。

2.3 TCP四次挥手:优雅地「说再见」

有连接就有断开。四次挥手,就是TCP断开连接的标准流程。为什么是四次?因为TCP是全双工的,双方都要独立关闭自己的数据通道。

流程是这样的:

  1. 第一次挥手:主动方(通常是客户端)发送FIN包,告诉对方「我没数据要发了」。
  2. 第二次挥手:被动方回复ACK,说「知道了,但我可能还有数据要发」。
  3. 第三次挥手:被动方发完所有数据后,发送FIN包,说「我也没数据了」。
  4. 第四次挥手:主动方回复ACK,说「收到,再见」。然后进入TIME_WAIT状态。

这里有个坑,我踩过不止一次——TIME_WAIT。主动关闭方在发送最后一个ACK后,不会立即关闭连接,而是等待2MSL(最大报文生存时间)后才真正释放。为什么?

说白了,就是为了防止最后一个ACK丢失。如果丢了,被动方会重发FIN,主动方还能再回复一次ACK。在高并发场景下,TIME_WAIT连接过多会耗尽端口资源。我曾经在线上看到一台服务器上有几万个TIME_WAIT连接,新连接死活建不起来。

避坑指南:高并发服务端,如果短连接特别多,一定要关注 netstat -n | grep TIME_WAIT 的数量。我曾经因为没注意这个,导致线上服务大面积超时。解决方案包括开启 tcp_tw_reuse 和 tcp_tw_recycle(但注意,tcp_tw_recycle在NAT环境下有坑,慎用)。

2.4 HTTP/HTTPS协议基础:应用层的「对话规则」

HTTP是应用层最常用的协议。说白了,它就是客户端和服务端之间约定好的「对话格式」。你请求什么资源,用什么方法,带什么头部,服务端返回什么状态码,都在HTTP协议里定义好了。

咱们看一个最简单的HTTP请求和响应:

// HTTP请求
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html

// HTTP响应
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234

<html>...</html>

HTTP本身是明文传输的。你抓包时,直接就能看到请求和响应的全部内容。这在早期互联网还行,但现在谁还敢用明文?所以就有了HTTPS。

HTTPS = HTTP + TLS/SSL。TLS在传输层之上、应用层之下,加了一层加密和认证。抓包时,你看到的HTTP内容全是密文,除非你配置了SSL解密(比如用Wireshark导入私钥或使用SSLKEYLOGFILE环境变量)。

我记得有一次帮客户排查一个HTTPS连接慢的问题。抓包一看,TLS握手阶段来回折腾了七八个包,比正常多了两倍。最后发现是客户端和服务端支持的密码套件不匹配,一直在协商降级。嗯,从那以后,我排查HTTPS性能问题时,第一件事就是看TLS握手花了几个RTT。

关键对比:

特性 HTTP HTTPS
默认端口 80 443
加密 无(明文) TLS/SSL加密
抓包可见性 直接看到全部内容 需要解密才能看到
性能开销 低 高(TLS握手+加解密)
安全性 低(易被窃听/篡改) 高(防窃听/防篡改/身份认证)

最后说一句,HTTP协议版本也在演进。HTTP/1.1有队头阻塞问题,HTTP/2通过多路复用解决了,但TCP层面仍有队头阻塞。HTTP/3直接换成了QUIC(基于UDP)。这些在高并发抓包时都会遇到,咱们后面的章节会详细展开。

嗯,协议基础就回顾到这儿。这些内容虽然基础,但每次抓包排查,最终都会回到这些最根本的原理上。你把这些搞透了,抓包时看到那些标志位、状态码、握手过程,心里就有底了。