当TCP的连接建立完成后,我们就可以尽情的通过TCP连接所创立的会话来进行数据的传输了。当然,再有意思的话题也有说完的时候,所以,当数据传输完之后,TCP该如何善后呢?

TCP的四次挥手

TCP的连接断开需要经历4次数据包的交互才能完成,所以这个过程我们习惯性的称为“四次挥手”。当然,结束连接之后,主机中的"资源"(即缓存和变量)也就被释放掉了。

TCP的的断开也是双方均可以主动发起,我们上图以客户端主动发起为例,来说明这个过程。

  1. 首先,客户端发现自己需要发送的数据已经发送完毕,有了断开TCP连接的想法。它会发出一个TCP报文段,这个报文段中的FIN标记位将置1。(这里注意,和TCP的三次握手不同,FIN报文段是允许携带数据的,所以,它可能会包含整个数据流中最后一段数据。)
  2. 服务器收到对方的FIN断开请求报文段,则回复一个ACK报文段。
  3. 等待服务器把需要发送的数据都处理完毕之后,服务器也会发送一个FIN断开请求的报文段给客户端。
  4. 客户端收到后,也将回复一个ACK作为最后的确认。至此,服务器将关闭TCP的连接。而客户端也将在等待一段时间(图示的2MSL)后关闭。(这个等待时间及等待原因,我们后面再分析)

以上便是TCP断开的一个正常流程了。我们这个过程中只关注了交互的方式,并没有关注具体数据包中的序列号和确认序列号,那是因为这几个数据包是允许包含数据的,所以,这个序列号和确认序列号需要考虑携带数据的字节数。

断开连接过程中的状态变化

同样,这个图中,也标识出了断开过程中客户端以及服务器的状态变化。我们还是分别从客户端和服务器的视角来看一下这个状态变化

我们还是先从客户端的角度出发,来观察下这个状态变化。

  1. 客户端一开始,还处于ESTABLISHED建立的状态,现在,他发现它数据传递完毕,于是发送了FIN断开请求报文段,之后,变进入到了一个新的状态 --- FIN_WAIT_1。
  2. 客户端再FIN_WAIT_1状态下,就是在等待服务器回复ACK,一旦等到之后,将进入到下一个状态。
  3. 客户端在收到服务器发送的ACK之后进入的状态被称为FIN_WAIT_2状态。这个状态主要就是等待服务器发送FIN请求。
  4. 客户端在收到服务器发送的FIN断开请求之后,将回复ACK进行确认。但注意,客户端并没有直接断开连接,进入到CLOSE(关闭)状态,而是进入到了一个TIME_WAIT状态继续等待。
  5. 在这个TIME_WAIT状态等待2MSL(这是一个时间),之后进入CLOSE(关闭)状态。进入到关闭状态后客户端将释放掉所有给这个TCP连接分配的资源。

以上便是客户端在断开连接过程中出现的状态变化。注意,服务器也可以主动发起断开,则服务器方的状态变化也将经历这几个过程。

下来我们再开看下服务器方的状态变化。

  1. 首先,服务器也还处在ESTABLISHED建立的状态。在收到客户端的FIN断开请求后,服务器会回复一个ACK进行确认,同时进入到CLOSED_WAIT状态
  2. 这个状态随着服务器自身发送FIN断开请求报文段之后,将结束,并进入到一个新的状态 --- LSA_ACK状态。
  3. 从这个状态的名字就可以看出来,服务器在等待最后的一个ACK报文段。当服务器接收到这个ACK之后,则将进入最终的状态,也就是CLOSE(关闭)状态。之后,将给这个TCP连接分配的所有资源释放掉。

以上便是整个“四次挥手”断开过程的状态变化。需要注意的就是,这里的客户端和服务器是相对的,先发送FIN的需要经历“客户端”那一套,也就是需要在关闭前进行等待。而被动方则走的是“服务器”那一套,再收到ACK报文之后直接关闭。

为什么需要有TIME_WAIT状态

不出意外的话,再看过TCP断开的四个过程中的状态变化之后,大家可能都会有一个疑问,就是这个FIN的发起方为啥需要等待一段时间之后,才能关闭TCP连接?也就是这个TIME_WAIT状态存在的意义是什么?

其实这是很重要的一个等待,因为他是我们连接能够正常被关闭的保证

我们先假设没有TIME_WAIT或者这个时间过短,看看会发生什么样的事情。

从上边的图中,我们其实就已经可以清楚的看出这个等待的必要性了。因为我们永远不能以最理想的眼光来看待网络通信。说不定,那个数据包就迷失在这网络数据之海中了。

这里假设客户端发送的最后一个ACK在网络中丢失了,而我们客户端不等待或者等待时间很短,则将自顾自的关闭了TCP连接。但是,服务端没有收到最后的确认,则一直停留在了LAST_ACK状态了。这样很不负责任。

服务器长时间接收不到ACK,则也将触发重传(TCP的可靠之处了),服务器会再发送一个FIN断开请求。而此时,客户端已经关闭了该连接通道,则将回复RST来终结连接,这就属于异常的断开

TIME_WAIT的时间为什么是2MSL

从上面的分析,我们可以看出来这个等待是必要的,当然,只需要发起方等待就可以了,因为它最后发送的ACK报文可能会出现问题,需要等待反馈。

而且,这个时间的长短也很重要,不能太短,否则服务器方的反馈还没有回来,你就关闭了,那还不如不等。这里定义的时间长短是2MSL。

 MSL --- Maximum Segment Lifetime。翻译过来叫做报文最大生存时间。这个时间指的是一个报文在网络上存在的最长时间,超过这个时间,则该数据报文将被丢弃。这个和IP头部中的TTL --- 生存时间还不一样,因为它是一个正儿八经的时间概念。

TTL是什么?

TTL被称为生存时间,是IP头部中定义的一个变量。它的用法是一个数据包,每经过一个路由器的转发,这个TTL值将减1,当一个数据包的TTL值被减为0的时候,则将不会被转发,而直接被丢弃。它并不是一个时间概念,反应的是经过路由的跳数。

所以,这个MSL的值应该大于等于TTL值消耗为0的值,以确保报文已被自然消亡。

那这里的2MSL指的就是2被的MSL时间。

TIME_WAIT的时间被设定为2MSL,也有有这方面的考虑的。这个数据包足够正常数据包的一个重传时间,比如如果被动关闭方没有收到断开连接的最后的ACK报文,就会触发超时重发Fin报文,另一方接收到FIN后(收到FIN断开请求后,客户端的TIME_WAIT状态会刷新,即等待时间重新计时2MSL),会重发ACK给被动关闭方,一来一去正好2个MSL。

Linux系统里,2MSL默认是60S

设置2MSL的TIME_WAIT时间还能应对一种情况,就是防止旧连接的数据包造成错乱。

这里也给大家一个场景。假设,服务器发送的一个数据报文在网络中被阻塞了,之后,客户端发起了断开连接的请求,同时请求的确认序列号应该是服务器之前丢失的那一个。此时,服务器将回复ACK并携带之前发送过的数据段进行重传。之后,TCP的连接正常断开。但注意,此时服务器之前被阻塞的数据段依旧在网络中慢悠悠的前进中。

恰巧,客户端有发起了一个新的连接。而之前这个数据包的序列号又恰好在新连接序号范围内,则将被客户端接收,造成数据错乱的后果。

所以,设计这2MSL的等待时间,足以让两个方向上的数据报都被丢弃,再出现新的连接时,不至于被历史报文造成数据错乱。

这里有一个点,需要大家注意一下。那就是客户端新建的连接使用的序列号问题。我们前面说过,客户端在发起连接时使用的随机的初始序列号ISN为了防止历史数据造成混乱,这个确实没错。但是,这个序列号并不是一个无限的值,其本质是由32位二进制构成的,所以其取值范围也是有限的。这个初始值在随机的时候,是基于时钟的,回滚一次需要大概4.55小时。但是,如果发送的数据量过大的话,是会加速这个回滚过程的。所以,是有可能出现上述场景的。

当然,这个时间也不能设置过长。时间太长必然导致内存资源的占用,并且,如果是连接发起方处在TIME_WAIT状态,则次状态下,还会导致其使用的端口资源被占用。(接收连接方也就是服务器方,理论上监听的同一个端口可以建立多个连接的,所以,不会造成端口占用的问题。当然,服务器如果TIME_WAIT状态的连接太多的话,也会大量的消耗系统资源,导致无法处理新的连接。)

为什么TCP的挥手需要四次而不能是三次

这是一个很经典的问题,不过其实也不难解释,如果认证看来前面断开的过程,其实就已经推理出一二了。

这个问题的核心其实在于"数据"上。理想的情况下,肯定是希望断开过程像连接建立过程一样,中间两步可以合成一步,变成“三次挥手”,这样,肯定是可以将资源节约下来的。但问题就是,你客户端(假设客户端是断开连接的发起方)数据发完了,我服务器(假设服务器是被动方)就也一定发完数据了吗?

所以,服务器发送FIN一定是取决于自己数据发送完毕的情况下,而不是为了赶给客户端回复ACK这个趟。

当然,也并不是说挥手就不能是三次。如果,恰好客户端发送FIN后,服务器回复的ACK中携带的是最后的数据报文段了,那同时将这个数据报文段中的FIN置1也未尝不可。那这样也就是三次挥手的情况了。

所以,这一切都在于“数据”是否被发送完毕。理论上的,我们还是要按照四次来进行理解记忆的。

【TCP专题】TCP连接断开相关推荐

  1. 协议簇:TCP 解析: 连接断开

    简介 接前文 协议簇:TCP 解析: 建立连接, 我们这篇文章来看看 TCP 连接断开的过程,也就是众所周知的"四次挥手"的具体流程. 系列文章 协议簇:TCP 解析:基础 协议簇 ...

  2. 协议簇:TCP 解析: 建立连接

    简介 接前文 协议簇:TCP 解析: 基础, 我们这篇文章来看看 TCP 连接建立的过程,也就是众所周知的"三次握手"的具体流程. 系列文章 协议簇:TCP 解析:基础 协议簇:T ...

  3. 【Java 网络编程】TCP 连接 断开 机制 ( 三次握手 | 四次挥手 )

    文章目录 I TCP 连接建立流程 ( 三次握手 ) II SYN 和 ACK 中的随机值 III TCP 连接建关闭流程 ( 四次挥手 ) IV TCP 连接断开的保证 V 四次挥手的必要性 I T ...

  4. go 监测tcp 连接断开_优化ngrok的tcp连接

    ngrok支持tcp tunnel和http以及https,但是ngrok的tcp代理似乎优化不够好,当一段时间闲置tcp连接的话,再连接会出现连接不上的问题. 首先来看看ngrok的tcp tunn ...

  5. python如何判断tcp异常断开_TCP socket如何判断连接断开

    http://blog.csdn.net/zzhongcy/article/details/21992123 SO_KEEPALIVE是系统底层的机制,用于系统维护每一个tcp连接的. 心跳线程属于应 ...

  6. [Qt] TCP客户端与服务器断开连接自动重联机制

    TCP服务器断开连接自动重联机制 客户端加入定时器实现断线重联(客户端服务端代码见上一篇博客) 编译环境:Qt 5.9.5 ui界面如图: 代码如下 tcpclient.h #ifndef TCPCL ...

  7. TCP连接断开原理剖析

    TCP连接三次握手 TCP协议在双方建立连接的时候需要三次握手, 所谓的三次握手即TCP连接的建立.这个连接必须是一方主动打开,另一方被动打开的.以下为客户端主动发起连接的图解: 其中比较重要的字段有 ...

  8. tcp要点学习-建立连接断开连接

    原文:http://www.cppblog.com/kevinlynx/archive/2008/05/14/49825.html 关于TCP的连接建立,断开的讲解,理论的核心内容基本提及,比较通俗. ...

  9. Linux数据链路tcp失败,TCP连接中的异常断开情况处理

    1.TCP连接中可能出现的异常断开情况 假设存在这样一种情况:在两个不同的主机Machine1.Machine2系统上分别运行两个应用程序Application1.Application2,在Appl ...

最新文章

  1. 【ASP.NET开发】ASP.NET(MVC)三层架构知识的学习总结
  2. OpenCV 笔记 -- 边缘检测(Sobel、Laplace、Canny)
  3. QT的QOpenGLShaderProgram类的使用
  4. 关于EXPORT_SYMBOL
  5. if和else同时执行_为什么大量的if else这么不受待见?怎么“干掉”它?
  6. jsf tree组件_JSF文本组件–标签,文本字段,文本区域和密码
  7. a的n次方的快速算法及大数相乘
  8. xml引用xsd文件规则
  9. mysql网上图书销售_PHP+MySQL网上书店|网上图书销售系统
  10. [UWP开发] Win10微博分享
  11. 怎样搜索计算机中docx格式的文件,如何打开docx文件 打开docx文件方法
  12. 蒙特卡洛 光 matlab,用蒙特卡罗法模拟光散射问题
  13. 一种电池过放电保护电路
  14. 抖音实战~搜索页面~扫描二维码
  15. 移动端产品设计:导航
  16. codewars练习(javascript)-2021/3/21
  17. CentOS7里ping命令详解
  18. 简单 sql 语句 实用大全
  19. 使用Charles做弱网测试入门篇
  20. 用Java写微信红包实现模拟微信发红包

热门文章

  1. java选择结构与分支结构
  2. 安卓自定义计时器控件
  3. 从loss的硬截断、软化到Focal Loss
  4. 在vue页面监听中如何修改子元素的样式
  5. python制作辅助和易语言的区别_为什么多数外挂都用易语言?
  6. 图片加载和它的内存们
  7. Android加载的图片在内存中的大小
  8. Python之数据库编程
  9. clientHeight offsetHeight scrollHeight offsetTop scrollTop
  10. python爬去新浪微博_荐爬虫实战 新浪微博爬取 详细分析