TCP三次握手和四次挥手

以一个初学角度深入研究TCP连接和释放连接的过程

从TCP报文开始,到实战wireshark抓包工具解析TCP三次握手和四次挥手

让你真正搞懂TCP三次握手和四次挥手

一、知识储备

要完全理解三次握手和四次挥手, 有必要从TCP报文格式开始理解

TCP报文格式

TCP每行报文分为4个字节, 32位

1.1 地址

第一行是地址, 分为源地址和目的地址, 该地址是由IP+端口组合的元组, 通过IP和端口唯一确定一个进程

1.2 序号Sequence Number

简称为seq,主要用来标识发送数据包的序号,占四个字节,32位

32位二进制可以表示的最大值为 4294967295(2^32-1),换算成文件大小约为4G,也就是说TCP在发送数据的时候可以一次给4GB以内的数据进行编号

比如浏览器请求一个html文档流,大小位100KB,而TCP一次只能发送25KB(取决于滑动窗口),因此需要分四次发送,第一次seq如果位0, 第二次seq就应该是25 * 1024 = 25600,依次递增,直至发完

CRC循环冗余法检测包正确性的时候也好用到这个seq,下面会具体分析

1.3 确认号Acknowledge Number

表示已收到某些数据包, 下次期望发送数据包序号,客户端ack就是期望服务端下次发送数据包的序号。比如我已经接受了0-100这一连串数据包,现在我应该接收101-200处的数据,因此ack位101,也就是客户端seq+1

1.4 标识位

URG:需要发送紧急指针时该值SET为 1

ACK:确认标识,代表收到请求的确认

PSH:代表数据优先级比较高,不在缓冲队列排队,直接发送给应用程序

RST:代表连接建立无效,需要重置(比如客户端一次发送了两个建立连接的请求,第一个已经成功建立TCP连接, 第二个建立连接的请求会被置为RST)

SYN:同步序号,建立连接时需要将SYN置为 1

FIN:释放连接信号量,当FIN为 1 时证明没有数据需要发送, 请求关闭连接

二、三次握手

通过抓包工具测试,因为通过浏览器访问网站有大量css,js等请求,看起来混乱,因此postman抓取首页,以下是我抓取的一个网站的首页

在wireshark中过滤host,过滤掉其余请求

wireshark抓包结果

这就是一次完整http请求,包含三次握手,四次挥手和数据传输

2.1 第一次握手

客户端发送建立链接请求,也就是抓到包的第一条,具体展开看一下

第一次握手

解析结果:

报文标志位中只有SYN标识为 1

生成一个初始化序列号 seq,第一次建立连接通常为0(相对seq)

生成一个初始化ack,通常为 0

还可以看到Window的值为64240(首屏优化可以考虑这个方面),这应该代表客户端一次可以接受的数据包大小 (滑动窗口)

客户端进入syn_sent状态

核心:SYN = 1, ack = 0,seq = 0, 客户端进入syn_sent状态,等待服务端响应

2.2 第二次握手

第二次握手

服务端收到建立连接请求,检查自身状态,同时发送一个确认数据包给客户端

将报文标志位SYNACK置为1,SYN表示这是建立连接的请求,ACK代表可以建立连接

生成服务端seq,未发送数据,通常也为 0

同时响应ack为(客户端seq + 1) = (0 + 1) = 1,表示期望客户端下次从ack位置开始发送数据

服务端进入syn_rcvd状态

核心:ACK = 1 SYN = 1 ack = 1, seq = 0(还未发送数据),进入syn_rcvd状态,等待客户端确认

2.3 第三次握手:

第三次握手

客户端发送确认连接数据包

将报文ACK标志位置为1,代表客户端确认请求已经连接,准备发送数据

ack为第二次握手服务端seq + 1,也就是 1

seq为第二次握手服务端ack + 1

客户端和服务端进入established状态

核心:客户端响应ACK = 1,seq = 1, ack = 1,建立了逻辑上的连接

三、数据传输

第三部分不关乎三次握手和四次挥手,无兴趣可直接跳过

3.1 发送HTTP

建立连接后客户端就可以发送HTTP请求了,这里请求的是首页,因此请求地址是网站的根 /

和前面捕获的几条报文不同, 这里还包含了应用层的HTTP报文,如请求的请求方法,协议,协议版本,自定义的请求头(Authorization)等,如下图

image-20220831151341485

HTTP是应用层协议,发送数据时依赖传输层的TCP协议,TCP负责传输数据,保证数据的可靠性,就像我们兢兢业业的程序员,而HTTP就是项目经理,和甲方以及公司领导对接

注意这里客户端发送的请求头,参数等长度为545,因此下次服务端响应的ack应该为546

3.2 响应HTTP请求

服务端收到请求后给客户端发送响应数据包(我收到你的请求了,准备给你发数据)

TCP响应3.3 服务端发送响应数据

服务端响应

四、四次挥手

看了很多文章都是说四次挥手,客户端发送FIN,服务端响应ACK,服务端发送FIN,客户端响应,完成四次挥手

实际抓包发现为了性能考虑,实际只有三次挥手,且听我狡辩

4.1 第一次挥手

第一次挥手

客户端将报文的FIN置为1,请求释放连接

客户端本应该只发FIN给服务端表示自己要释放连接,但是这里确还有个ACK,这里的ACK应该是为了响应服务端的HTTP响应结果,因为TCP是可靠连接,所以事事有回应,又不想浪费空间,就两次合并到一个报文中

4.2 第二、三次挥手

第二次挥手

这里本该是服务端两个报文, 第一个报文响应客户端FIN,第二个报文发送FIN给客户端, 二者也合并了,因此发送的报文就是ACK + FIN,既满足了可靠,还节省了一次报文

4.3 第四次挥手

第三次挥手

客户端响应ACK报文,连接释放,客户端和服务端都进入等待状态,等待下一次连接

五、总结

很多课本、网课甚至博客都在说三次握手四次挥手,不经过抓包还真傻傻的以为是四次挥手

纸上得来终觉浅,绝知此事要躬行!!