网络协议基础回顾:TCP三次握手与四次挥手、HTTP/1.1与HTTP/2的区别、连接池原理

做后端抓包分析,不懂网络协议基础,就像修车不看发动机。你抓到的每一个数据包,背后都是协议在“说话”。今天咱们就把这三个核心概念掰开揉碎,讲清楚。

TCP三次握手:建立连接的“敲门”过程

TCP是面向连接的协议。说白了,在发数据之前,双方得先“打个招呼”,确认对方在线、愿意收数据。这个打招呼的过程,就是三次握手。

核心流程:
  1. 第一次握手(SYN):客户端发送一个SYN包(同步序列号),告诉服务端:“我想跟你建立连接,我的初始序列号是X。”
  2. 第二次握手(SYN+ACK):服务端收到后,回复一个SYN+ACK包,表示:“收到你的请求,我的初始序列号是Y,并且确认你的序列号X+1。”
  3. 第三次握手(ACK):客户端再发一个ACK包,确认收到服务端的SYN:“好的,确认你的序列号Y+1。咱们开始传数据吧。”

为什么是三次,不是两次?我刚开始做开发时也困惑过。后来在一次抓包分析中才真正理解:如果只有两次握手,服务端无法确认客户端是否收到了自己的SYN+ACK。万一丢包了,服务端会一直傻等,浪费资源。三次握手,本质上是双方互相确认“收发能力”都正常。

抓包小技巧:在Wireshark中,过滤表达式 tcp.flags.syn == 1 可以快速定位握手包。注意观察序列号(Seq)和确认号(Ack)的变化,它们能帮你判断握手是否正常完成。

TCP四次挥手:优雅地“说再见”

数据传输完了,需要断开连接。TCP是全双工的,所以断开时,双方都要“说再见”。这个过程就是四次挥手。

核心流程:
  1. 第一次挥手(FIN):主动关闭方(比如客户端)发送一个FIN包,表示:“我这边数据发完了,准备关闭。”
  2. 第二次挥手(ACK):被动关闭方(服务端)回复ACK,表示:“收到你的FIN,我这边还有数据要发,你先等着。”
  3. 第三次挥手(FIN):被动关闭方数据发完后,发送FIN包,表示:“我这边也发完了,可以关了。”
  4. 第四次挥手(ACK):主动关闭方回复ACK,表示:“收到,关闭连接。”

这里有个坑,我曾经踩过。主动关闭方发送最后一个ACK后,会进入TIME_WAIT状态,等待2MSL(最大报文段生存时间)后才真正关闭。为什么?因为要确保最后一个ACK能被对方收到。如果ACK丢了,对方会重发FIN,而TIME_WAIT状态能处理这种重传。在高并发场景下,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的多路复用,才是真正的解决方案。

抓包对比:在Wireshark中,HTTP/1.1的请求是串行的,你可以看到连续的GET请求,每个请求都要等前一个完成。而HTTP/2的请求是并行的,多个请求的帧交织在一起。过滤表达式 http2 可以快速定位HTTP/2流量。

连接池原理:复用连接,减少开销

每次建立TCP连接,都要经历三次握手,开销不小。连接池的核心思想就是:把建立好的连接缓存起来,下次请求直接复用,避免重复握手。

连接池的工作流程:
  1. 初始化:创建一定数量的连接,放入池中。
  2. 获取连接:请求到来时,从池中取一个空闲连接。
  3. 使用连接:发送请求,接收响应。
  4. 归还连接:使用完毕后,将连接放回池中,供下次使用。
  5. 连接管理:定期检查连接的健康状态,清理失效连接,创建新连接补充。

连接池的几个关键参数,我建议你重点关注:

  • 最大连接数:池中最多能有多少个连接。设置太小,请求会排队;设置太大,会耗尽系统资源。
  • 最小空闲连接数:池中保持的最小空闲连接数。避免突发流量时临时创建连接。
  • 连接超时时间:连接空闲多久后会被回收。避免长时间占用资源。
  • 获取连接超时时间:请求等待获取连接的最大时间。超过这个时间,直接报错,避免无限等待。
避坑指南:我曾经遇到一个线上问题,数据库连接池配置了最大连接数100,但业务高峰期时,请求频繁超时。抓包发现,连接池中的连接被长时间占用,导致新请求无法获取连接。原因是某个慢查询占用了连接,迟迟不归还。解决方案是:设置合理的连接超时时间,并监控慢查询,及时kill掉。

连接池的实现,常见的有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协议版本,最后检查连接池配置。这样一步步排查,问题往往能快速定位。

TCP三次握手与四次挥手流程 TCP三次握手与四次挥手流程 客户端 服务端 ① SYN (Seq=X) ② SYN+ACK (Seq=Y, Ack=X+1) ③ ACK (Seq=X+1, Ack=Y+1) 数据传输 ④ FIN (Seq=U) ⑤ ACK (Ack=U+1) ⑥ FIN (Seq=V) ⑦ ACK (Ack=V+1) 客户端进入 TIME_WAIT 状态