TCP状态转换图详解

之前已经介绍了TCP协议的三路握手和四次挥手。如下图所示,TCP通信过程包括三个步骤:建立TCP连接通道(三次握手)、数据传输、断开TCP连接通道(四次挥手)。

                                                

这里进一步探究TCP三路握手和四次挥手过程中的状态变迁以及数据传输过程。先看TCP状态状态转换图。

ç¶æ转æ¢å¾

 

1. 建立连接(three-way hand shake)

  • 主动打开(passive open):服务器必须准备好接受外来的连接,通常通过socket、bind和listen完成。
  • 被动打开(active open):客户端通过connect发起主动打开。

插句话,该文介绍的性能分析工具(sar -n TCP,ETCP 1)命令可以对主动打开和被动打开的数目进行统计,在一定程度上可以反应服务器的繁忙程度。

下图给出客户端与服务器的三次握手过程:

三次握手过程

  1. 服务器准备好接受外来连接,通常通过socket、bind和listen完成。(服务器:CLOSED->LISTEN)
  2. 客户端通过connect连接服务器,客户端TCP将发送一个SYN包,告诉服务器客户端将在待建立连接发送数据的初始序列号。(客户端:CLOSED->SYN_SENT)
  3. 服务器端必须ACK客户端SYN,同时发送一个SYN,告诉客户端,服务器将在待建立连接发送数据的初始序列号。(服务器:SYN_RCVD)
  4. 客户端必须ACK服务器SYN。(客户端:SYN_SENT->ESTABLISHED)
  5. 服务器接收到客户端ACK。(服务器:SYN_RECV->ESTABLISHED)

下图给出三次握手对应的状态转移图:

状态转移图

2. 建立连接(同时打开simultaneous open)

参考《TCP/IP详解》卷一第18章18.8节。这种情况发生在两端几乎同时发送SYN并且这两个SYN在网络中交错的情形。这种情况可能发生,但是非常罕见。

例如,主机A的应用程序使用本地端口7777,并与主机B的端口8888执行主动打开。主机B的应用程序使用本地端口8888,并与主机A的端口7777执行主动打开。

下图给出同时打开过程:

同时打开过程

下图给出同时打开的状态转移图:

状态转换图

说明:从目前个人经验来看,这种场景没有遇到过,但这是完整理解TCP状态转移必须的一部分。但《TCP/IP详解》卷一第18章18.8节中支出,许多伯克利版的TCP实现都不能正确地支持打开。

3. 建立连接失败(1)

考虑场景:客户端尚未接收到服务器ACK+SYN,异常退出(崩溃退出)。这时,当客户端接收到服务器的ACK+SYN时,客户端回复RST(reset)。此时服务器处于SYN_RCVD状态,当接收到客户端RST时,则从SYN_RCVD转移到LISTEN状态。

具体过程:

  1. 服务器准备好接受外来连接,通常通过socket、bind和listen完成。(服务器:CLOSED->LISTEN)
  2. 客户端通过connect连接服务器,客户端TCP将发送一个SYN包,告诉服务器,客户端将在待建立连接发送数据的初始序列号。然后客户端异常退出。(客户端:CLOSED->SYN_SENT,然后突然crash,则退出SYN_SENT)
  3. 服务器端ACK客户端SYN,同时发送一个SYN,告诉客户端,服务器将在待建立连接发送数据的初始序列号。(服务器:SYN_RCVD)
  4. 客户端找不到服务器ACK+SYN对应的SYN_SENT状态的socket,则响应RST。(服务器:SYN_RCVD->LISTEN)

下图给出建立连接失败的状态转移图:

状态转换图

4. 建立连接失败(2)

考虑场景1:服务器的进程异常退出,客户端不知道。那么客户端发送SYN后,服务器端响应RST,则客户端建立连接失败。

考虑场景2:服务器机器关闭,导致服务器IP不可达。那么客户端发送SYN后,超时重发,超过重试次数,最终TIMEOUT,则客户端建立连接失败。

具体过程:

  1. 假设服务器进程退出。(服务器:LISTEN->CLOSED)
  2. 客户端通过connect连接服务器,客户端TCP将发送一个SYN包,告诉服务器,客户将在待建立连接发送数据的初始序列号。(客户端:CLOSED->SYN_SENT)
  3. 服务器端收到客户端SYN,响应RST(服务器:CLOSED)
  4. 客户收到RST。(客户端:SYN_SENT->CLOSED)

下图给出建立连接失败的状态转移图:

状态转换图

5. 断开连接(四次挥手)

  • 主动关闭(active close):某个应用程序首先调用close,发送一个FIN包。
  • 被动关闭(passive close):接收到FIN的对端执行被动关闭。

下图给出客户端与服务器的四次挥手过程:

四次挥手

  1. 某个应用程序首先调用close,该端发送一个FIN包,表示数据发送完毕,该应用程序再无更多数据发送给对端。(例如HTTP服务器发送Reponse数据给client后,再无多余数据发送,则Server可以执行主动关闭)(主动端:ESTABLISHED->FIN_WAIT_1)
  2. 接收到FIN的对端执行被动关闭。首先ACK这个收到的FIN包。该FIN包的接收也作为一个文件结束符(EOF)传递给应用程序(放在已排队等候该应用进程接收的任何其他数据之后),因为FIN包意味着接收端应用进程在相应的连接上再无额外数据可接收。(被动端:ESTABLISHED->CLOSE_WAIT,主动端:FIN_WAIT_1->FIN_WAIT_2)
  3. 一段时间后,接收到这个EOF的应用进程将调用close关闭socket。这导致它的TCP也发送一个FIN包。(被动端:CLOSE_WAIT->LAST_WAIT)  所以CLOSE_WAIT这个状态是让服务器发送还未传送完的数据。
  4. 接收这个最终FIN的执行主动关闭的那一端ACK这个FIN。(被动端:LAST_WAIT->CLOSED,主动端:FIN_WAIT_2->TIME_WAIT(2MSL之后,TIME_WAIT->CLOSED))

下图给出四次挥手的状态转移图:

状态转换图

6. 断开连接(同时关闭simultaneous close)

参考《TCP/IP详解》卷一第18章18.9节。我们在前面讨论了一方(通常但不总是客户方)发送第一个FIN执行主动关闭。双方都执行主动关闭也是可能的,TCP协议也允许这样的同时关闭(simultaneous close)。

下图给出同时关闭过程:

同时打开过程

下图给出同时关闭的状态转移图:

状态转换图

7. 断开连接(在FIN_WAIT_1状态中,接收FIN+ACK)

考虑场景:被动关闭端收到FIN包后,直接发送FIN+ACK,则主动关闭方则从FIN_WAIT_1跳过FIN_WAIT_2,直接进入TIME_WAIT。

给出具体流程:

  1. 某个应用程序首先调用close,该端发送一个FIN包,表示数据发送完毕,该应用程序再无更多数据发送给对端。(例如HTTP服务器发送Reponse数据给client后,再无多余数据发送,则Server可以执行主动关闭)(主动端:ESTABLISHED->FIN_WAIT_1)
  2. 接受到FIN的对端执行被动关闭。收到FIN包之后,被动端调用close关闭socket,则FIN+ACK同时发给主动端。(被动端:ESTABLISHED->CLOSE_WAIT->LAST_ACK,主动端:FIN_WAIT_1->TIME_WAIT)
  3. 接收这个最终FIN的执行主动关闭的那一端ACK这个FIN。(被动端:LAST_WAIT->CLOSED,主动端:FIN_WAIT_1->TIME_WAIT(2MSL之后,TIME_WAIT->CLOSED))

下图给出同时关闭的状态转移图:

状态转换图

 关于TIME_WAIT需要等2MSL时间才能回到CLOSED状态

 TIME_WAIT状态存在的原因有两点:

1.可靠的终止TCP连接

2.保证让迟来的TCP报文段有足够的时间被识别并丢弃

第一点很好解释:如果网络不可靠,那么就无法保证最后客户端发送的ACK报文服务器端一定能够收到,因此处于LAST_ACK状态的服务器可能会因为超时而未收到ACK报文,而重新向客户端发送FIN报文。因此客户端需要停留在TIME_WAIT状态一段时间以处理重复收到的报文段。如果没有这个TIME_WAIT状态,客户端处于CLOSED状态,那么客户端将响应RST(reset),服务器端收到后会将该RST分节解释成一个错误,也就不能实现最后的全双工关闭了(主动方单方的关闭)。所以用TIME_WAIT状态来保证TCP连接的可靠终止。

第二个原因:比如在客户端收到ACK后如果立即关闭,虽然这个端口已经关闭,但如果有一个新的连接被建立起来,使用的IP地址和端口和这个先前到达了CLOSED状态的完全相同,假定原先的连接中还有数据报残存在网络之中,这样新的连接建立以后传输的数据极有可能就是原先的连接的数据报,为了防止这一点,TCP不允许从处于TIME_WAIT状态的socket 建立一个连接,处于TIME_WAIT状态的 socket 在等待了两倍的MSL时间之后,将会转变为CLOSED状态。这里TIME_WAIT状态持续的时间是2MSL(MSL是任何IP数据报能够在因特网中存活的最长时间),足以让这两个方向上的数据包被丢弃(最长是2MSL)。通过实施这个规则,我们就能保证每成功建立一个TCP连接时,来自该连接先前化身的老的重复分组都已经在网络中消逝了。

综上来看:TIME_WAIT存在的两个理由就是

  1. 可靠地实现TCP全双工连接的终止;
  2. 让老的重复分节(数据报)在网络中消逝。

--------------------- 

本文参考:

1.https://www.cnblogs.com/figo-cui/p/5137993.html 

2.https://blog.csdn.net/wenqian1991/article/details/40110703

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]:Linux内核实现了很多重要的体系结构属性。在或高或低的层次上,内核被划分为多个子系统。Linux也可以看作是一个整体,因为它会将所有这些基本服务都集成到内核中。这与微内核的体系结构不同,后者会提供一些基本的服务,例如通信、I/O、内存和进程管理,更具体的服务都是插入到微内核层中的。\[2\]为了解决这些缺陷,Linux内核开发人员所做的一件事就是使内核模块可以在运行时加载和卸载,这意味着您可以动态地添加或删除内核的特性。这不仅可以向内核添加硬件功能,还可以包括运行服务器进程的模块,比如低级别虚拟化,但也可以替换整个内核,而不需要在某些情况下重启计算机。\[3\] 根据引用\[1\]和引用\[3\]的内容,Linux内核采用了宏内核的机制,将所有的代码和子系统打包到一个文件中。这意味着内核中的每一个函数都可以访问到内核中的其他部分。同时,Linux内核也支持模块的动态装卸,可以在运行时加载和卸载内核模块,从而动态地添加或删除内核的特性。 关于TCP状态转换的内核原理,具体的实现细节可能涉及到更多的技术细节。但是可以简单地说,TCP状态转换是指TCP连接在不同的状态之间进行转换,例如建立连接、数据传输、连接关闭等。在Linux内核中,TCP状态转换是通过TCP协议栈的实现来完成的。TCP协议栈是Linux内核中的一个子系统,负责处理TCP连接的建立、维护和关闭等操作。具体的实现细节可以参考Linux内核的源代码和相关文档。 总结起来,Linux内核采用了宏内核的机制,将所有的代码和子系统打包到一个文件中,并支持模块的动态装卸。TCP状态转换是通过TCP协议栈的实现来完成的。 #### 引用[.reference_title] - *1* *2* *3* [一文看懂Linux内核!Linux内核架构和工作原理详解](https://blog.csdn.net/qq_40989769/article/details/113388892)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值