tcp链接中的三次挥手是什么原因

一、tcp链接中的正常四次挥手过程?

刚开始双方都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下:

1、客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。2、接着,当服务端在 read 数据的时候,最后⾃然就会读到 EOF,接着 read() 就会返回 0 ,这时服务端应⽤程序如果有数据要发送的话,就发完数据后才调⽤关闭连接的函数,如果服务端应⽤程序没有数据要发送的话,可以直接调⽤关闭连接的函数,这时服务端就会发⼀个 FIN 包 ,这个 FIN 报⽂代表服务端不会再发送数据了,之后处于 LAST_ACK 状态;。

3、客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。

4、等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。

5、客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态

6、服务端收到了 ACK 应答报文后,就进入了 CLOSE 状态,至此服务端已经完成连接的关闭。

7、客户端在经过 2MSL 一段时间后,自动进入 CLOSE 状态,至此客户端也完成连接的关闭。

你可以看到,每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。

这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。

二、为什么挥手需要四次

服务器收到客户端的 FIN 报⽂时,内核会⻢上回⼀个 ACK 应答报⽂,但是服务端应⽤程序可能还有数据要发送,所以并不能⻢上发送 FIN 报⽂,⽽是将发送 FIN 报⽂的控制权交给服务端应⽤程序

1、关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。

2、服务端收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。

从上⾯过程可知,是否要发送第三次挥⼿的控制权不在内核,⽽是在被动关闭⽅(上图的服务端)的应⽤程序,因为应⽤程序可能还有数据要发送,由应⽤程序决定什么时候调⽤关闭连接的函数,当调⽤了关闭连接的函数,内核就会发送 FIN 报⽂了,所以服务端的 ACK 和 FIN ⼀般都会分开发送。

FIN 报⽂⼀定得调⽤关闭连接的函数,才会发送吗?不一定

如果进程退出了,不管是不是正常退出,还是异常退出(如进程崩溃),内核都会发送 FIN 报⽂,与对⽅完成四次挥⼿。

三、粗暴关闭 vs 优雅关闭

前⾯介绍 TCP 四次挥⼿的时候,并没有详细介绍关闭连接的函数,其实关闭的连接的函数有两种函数:

  • close 函数,同时 socket 关闭发送⽅向和读取⽅向,也就是 socket 不再有发送和接收数据的能⼒。如果有多进程/多线程共享同⼀个 socket,如果有⼀个进程调⽤了 close 关闭只是让 socket 引⽤计数-1,并不会导致 socket 不可⽤,同时也不会发出 FIN 报⽂,其他进程还是可以正常读写该 socket, 直到引⽤计数变为 0,才会发出 FIN 报⽂。
  • shutdown 函数,可以指定 socket 只关闭发送⽅向⽽不关闭读取⽅向,也就是 socket 不再有发送数据的能⼒,但是还是具有接收数据的能⼒。如果有多进程/多线程共享同⼀个 socket,shutdown 则不 管引⽤计数,直接使得该socket 不可⽤,然后发出 FIN 报⽂,如果有别的进程企图使⽤该 socket, 将会受到影响。

如果客户端是⽤ close 函数来关闭连接,那么在 TCP 四次挥⼿过程中,如果收到了服务端发送的数据,由于客户端已经不再具有发送和接收数据的能⼒,所以客户端的内核会回 RST 报⽂给服务端,然后内核会释放连接,这时就不会经历完成的 TCP 四次挥⼿,所以我们常说,调⽤ close 是粗暴的关闭。

当服务端收到 RST 后,内核就会释放连接,当服务端应⽤程序再次发起读操作或者写操作时,就能感知到连接已经被释放了:

  • 如果是读操作,则会返回 RST 的报错,也就是我们常⻅的Connection reset by peer。
  • 如果是写操作,那么程序会产⽣ SIGPIPE 信号,应⽤层代码可以捕获并处理信号,如果不处理,则默 认情况下进程会终⽌,异常退出。

相对的,shutdown 函数因为可以指定只关闭发送⽅向⽽不关闭读取⽅向,所以即使在 TCP 四次挥⼿过程中,如果收到了服务端发送的数据,客户端也是可以正常读取到该数据的,然后就会经历完整的 TCP 四次挥⼿,所以我们常说,调⽤ shutdown 是优雅的关闭。

但是注意,shutdown 函数也可以指定「只关闭读取⽅向,⽽不关闭发送⽅向」,但是这时候内核是不会发送 FIN 报⽂的,因为发送 FIN 报⽂是意味着我⽅将不再发送任何数据,⽽ shutdown 如果指定「不关闭发送⽅向」,就意味着 socket 还有发送数据的能⼒,所以内核就不会发送 FIN。

四、什么情况会出现三次挥⼿?

当被动关闭⽅(上图的服务端)在 TCP 挥⼿过程中,「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第⼆和第三次挥⼿就会合并传输,这样就出现了三次挥⼿

然后因为 TCP 延迟确认机制是默认开启的,所以导致我们抓包时,看⻅三次挥⼿的次数⽐四次挥⼿还多。

什么是 TCP 延迟确认机制?

当发送没有携带数据的 ACK,它的⽹络效率也是很低的,因为它也有 40 个字节的 IP 头 和 TCP 头,但却没有携带数据报⽂。为了解决 ACK 传输效率低问题,所以就衍⽣出了 TCP 延迟确认。

TCP 延迟确认的策略:

  • 当有响应数据要发送时,ACK 会随着响应数据⼀起⽴刻发送给对⽅
  • 当没有响应数据要发送时,ACK 将会延迟⼀段时间,以等待是否有响应数据可以⼀起发送
  • 如果在延迟等待发送 ACK 期间,对⽅的第⼆个数据报⽂⼜到达了,这时就会⽴刻发送 ACK
相关推荐
炙热的大叔16 分钟前
JavaEE初阶-网络编程
网络·java-ee
量化交易学徒1 小时前
403 Forbidden:深入解析 HTTP 禁止访问错误及排查方法
网络·网络协议·http
苦藤新鸡1 小时前
用网络编程完成windows和linux跨平台之间的通信(服务器)
linux·网络·windows
听楷哥说跨境1 小时前
电商视角如何理解动态IP与静态IP
tcp/ip·ip
huaqianzkh2 小时前
传统数据处理系统存在的问题
网络·数据库·系统架构
Tassel_YUE4 小时前
iptables配置实现NAT(随手记)
linux·运维·服务器·网络·iptables
WolvenSec7 小时前
网络基础:EIGRP
运维·网络·安全·智能路由器
开开心心kai9 小时前
如何快速申请免费SSL证书,实现网站HTTPS安全传输
网络·网络协议·安全·https·ssl
Geeker559 小时前
如何在忘记密码的情况下解锁Android手机?
android·网络·macos·华为·智能手机·电脑·手机
这是另一个世界12 小时前
黑客技术大纲
网络·学习·web安全·网络安全