网络协议基础回顾:TCP三次握手与四次挥手、HTTP/1.1与HTTP/2的区别、连接池原理
做后端抓包分析,不懂网络协议基础,就像修车不看发动机。你抓到的每一个数据包,背后都是协议在“说话”。今天咱们就把这三个核心概念掰开揉碎,讲清楚。
TCP三次握手:建立连接的“敲门”过程
TCP是面向连接的协议。说白了,在发数据之前,双方得先“打个招呼”,确认对方在线、愿意收数据。这个打招呼的过程,就是三次握手。
- 第一次握手(SYN):客户端发送一个SYN包(同步序列号),告诉服务端:“我想跟你建立连接,我的初始序列号是X。”
- 第二次握手(SYN+ACK):服务端收到后,回复一个SYN+ACK包,表示:“收到你的请求,我的初始序列号是Y,并且确认你的序列号X+1。”
- 第三次握手(ACK):客户端再发一个ACK包,确认收到服务端的SYN:“好的,确认你的序列号Y+1。咱们开始传数据吧。”
为什么是三次,不是两次?我刚开始做开发时也困惑过。后来在一次抓包分析中才真正理解:如果只有两次握手,服务端无法确认客户端是否收到了自己的SYN+ACK。万一丢包了,服务端会一直傻等,浪费资源。三次握手,本质上是双方互相确认“收发能力”都正常。
tcp.flags.syn == 1 可以快速定位握手包。注意观察序列号(Seq)和确认号(Ack)的变化,它们能帮你判断握手是否正常完成。
TCP四次挥手:优雅地“说再见”
数据传输完了,需要断开连接。TCP是全双工的,所以断开时,双方都要“说再见”。这个过程就是四次挥手。
- 第一次挥手(FIN):主动关闭方(比如客户端)发送一个FIN包,表示:“我这边数据发完了,准备关闭。”
- 第二次挥手(ACK):被动关闭方(服务端)回复ACK,表示:“收到你的FIN,我这边还有数据要发,你先等着。”
- 第三次挥手(FIN):被动关闭方数据发完后,发送FIN包,表示:“我这边也发完了,可以关了。”
- 第四次挥手(ACK):主动关闭方回复ACK,表示:“收到,关闭连接。”
这里有个坑,我曾经踩过。主动关闭方发送最后一个ACK后,会进入TIME_WAIT状态,等待2MSL(最大报文段生存时间)后才真正关闭。为什么?因为要确保最后一个ACK能被对方收到。如果ACK丢了,对方会重发FIN,而TIME_WAIT状态能处理这种重传。在高并发场景下,TIME_WAIT过多会导致端口耗尽,这是抓包时经常遇到的问题。
net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_tw_recycle(注意:tcp_tw_recycle在NAT环境下有坑,慎用),或者改用连接池复用连接。
HTTP/1.1与HTTP/2的区别:从“排队”到“多路复用”
HTTP/1.1和HTTP/2,本质上是传输效率的差异。我做个比喻你就明白了:HTTP/1.1是单车道,一次只能过一辆车;HTTP/2是多车道,可以同时过很多辆车。
| 特性 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 连接复用 | 支持持久连接(Keep-Alive),但请求必须串行(队头阻塞) | 多路复用,一个连接上可以并行多个请求 |
| 队头阻塞 | 存在。一个请求阻塞,后续请求都得等 | 不存在。多个请求可以同时发送和接收 |
| 头部压缩 | 无。每次请求都携带完整头部 | 有。使用HPACK算法压缩头部,减少冗余 |
| 服务器推送 | 不支持 | 支持。服务器可以主动推送资源给客户端 |
| 二进制分帧 | 文本协议 | 二进制协议,解析效率更高 |
你想想看,HTTP/1.1的队头阻塞有多烦人?一个页面加载几十个资源,每个资源都要排队等。虽然可以用域名分片(多个域名)来缓解,但治标不治本。HTTP/2的多路复用,才是真正的解决方案。
http2 可以快速定位HTTP/2流量。
连接池原理:复用连接,减少开销
每次建立TCP连接,都要经历三次握手,开销不小。连接池的核心思想就是:把建立好的连接缓存起来,下次请求直接复用,避免重复握手。
- 初始化:创建一定数量的连接,放入池中。
- 获取连接:请求到来时,从池中取一个空闲连接。
- 使用连接:发送请求,接收响应。
- 归还连接:使用完毕后,将连接放回池中,供下次使用。
- 连接管理:定期检查连接的健康状态,清理失效连接,创建新连接补充。
连接池的几个关键参数,我建议你重点关注:
- 最大连接数:池中最多能有多少个连接。设置太小,请求会排队;设置太大,会耗尽系统资源。
- 最小空闲连接数:池中保持的最小空闲连接数。避免突发流量时临时创建连接。
- 连接超时时间:连接空闲多久后会被回收。避免长时间占用资源。
- 获取连接超时时间:请求等待获取连接的最大时间。超过这个时间,直接报错,避免无限等待。
连接池的实现,常见的有Apache HttpClient、OkHttp、Jedis等。它们的原理大同小异,但细节上各有千秋。比如,OkHttp的连接池默认支持5个空闲连接,每个连接空闲5分钟后会被回收。而HttpClient的连接池则更灵活,可以自定义各种参数。
// OkHttp连接池示例
ConnectionPool pool = new ConnectionPool(5, 5, TimeUnit.MINUTES);
OkHttpClient client = new OkHttpClient.Builder()
.connectionPool(pool)
.build();
// HttpClient连接池示例
PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
manager.setMaxTotal(200);
manager.setDefaultMaxPerRoute(20);
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(manager)
.build();
嗯,这里要注意:连接池不是万能的。如果连接池中的连接被服务端主动关闭(比如服务端设置了连接超时),客户端再使用这个连接时,会报错。所以,连接池需要配合心跳检测或重试机制。
本章小结
TCP三次握手和四次挥手,是网络通信的基础。理解它们,你才能看懂抓包中的SYN、ACK、FIN包。HTTP/1.1和HTTP/2的区别,决定了你的应用性能。连接池,则是优化网络性能的利器。这三者结合起来,你就能从网络层面定位并发瓶颈。
我个人习惯,在抓包分析时,先看TCP握手是否正常,再看HTTP协议版本,最后检查连接池配置。这样一步步排查,问题往往能快速定位。