【网络原理】TCP协议的连接管理机制(三次握手和四次挥手)

发布于:2024-04-29 ⋅ 阅读:(25) ⋅ 点赞:(0)

系列文章目录

【网络通信基础】网络中的常见基本概念

【网络编程】网络编程中的基本概念及Java实现UDP、TCP客户端服务器程序(万字博文)

【网络原理】UDP协议的报文结构 及 校验和字段的错误检测机制(CRC算法、MD5算法)

【网络原理】TCP协议的相关机制(确认应答、超时重传)


文章目录

前言

一、建立连接(三次握手)

建立连接(三次握手)的意义

1)投石问路,确认当前通信路径是否畅通

2)验证通信双方,发送能力和接收能力是否正常

3)三次握手的过程中,通信双方也会协商一些必要的参数

二、断开连接(四次挥手)

三、连接管理过程中涉及到的TCP状态转换


前言

接上文,我们介绍了TCP协议中,保证可靠性传输的核心机制,确认应答和超时重传机制。

TCP协议的连接管理机制,是整个网络原理中,最高频的问题,没有之一!因此,这里单独用一篇博客,详细介绍TCP协议的连接管理机制。

TCP协议的连接管理机制主要包括三次握手建立连接四次挥手断开连接


一、建立连接(三次握手)

在前面的文章中,我们写过TCP回显客户端和服务器程序。

当客户端执行 clientSocket = new Socket(serverIp, serverPort); 这个操作的时候,就是在建立连接。此处只是调用了 socket API,真正连接建立的过程,是由TCP协议完成的。

TCP协议建立连接的这个过程,就称为“三次握手”。

这里谈到的连接,是“虚拟的,抽象的”连接。目的是让通信双方都能保存对方的相关信息。

此处又涉及到六位标志位的其中一位=>SYN,代表同步(synchronize)。所谓的SYN,是一个特殊的,带有SYN标志位的TCP数据包:

  1. 没有载荷,不带有应用层的数据.
  2. 六位标志位的第五位,将该位设置为1.

虽然SYN数据包不带有载荷,但是SYN数据包会包含IP报头(如果在IP网络中传输)或以太网数据帧帧头(如果在以太网中传输),以及TCP报头。这些头部包含了一些必要的信息,比如源IP地址、目标IP地址、源端口、目标端口等。这个过程,也是客户端在告诉服务器,我是谁。

了解了SYN数据包后,再来看下三次握手的大致过程。首先要清楚,客户端是主动的一方,第一次交互,一定有客户端主动发起的:

上图过程中,是有四次交互,简单来说:

  1. 客户端先向服务器发送SYN请求
  2. 服务器回复客户端一个ACK应答报文
  3. 服务器再给客户端发送一个SYN请求
  4. 客户端回复服务器一个ACK应答报文

这个建立连接的过程,本质上就是通信双方各自给对方发起一个SYN,各自给对方回应一个ACK。但实际上,中间的两次交互(2和3),是能够合二为一的。

因为回复一个带ACK标志位的数据包,再回复一个带SYN标志位的数据包,可以合并成一个数据包,这个数据包同时带有ACK标志位和SYN标志位。即第2位(ACK)和第5位(SYN)都为1.

此时这个数据包就同时起到了两个作用,既能应答上个请求,也能发起SYN。

这么做的原因是:网络传输过程中要涉及到多次的封装和分用,两个包合并成一个包,就可以减少一次封装分用的过程,整体的效率就提升了。

经过合并,四次交互也就变成了三次,也就形成了“三次握手”


因此,三次握手的具体过程如下:

  1. 客户端向服务器发送SYN请求: 客户端首先向服务器发送一个带有SYN标志位的数据包,其中包含了客户端的初始序列号(ISN)。

  2. 服务器发送SYN-ACK响应: 服务器收到客户端的SYN请求后,会回复一个带有SYN和ACK标志位的数据包,称为SYN-ACK响应。这个响应中,服务器确认了客户端的SYN请求,并指定了服务器的初始序列号(ISN)。

  3. 客户端发送ACK确认: 最后,客户端收到服务器的SYN-ACK响应后,会发送一个带有ACK标志位的数据包,表示确认了服务器的响应。这个ACK数据包中,客户端会确认收到了服务器的SYN响应,并指定了下一个要发送的序列号。

至此,三次握手完成,TCP连接建立成功,双方可以开始进行数据传输了。

前面给出的都是简图,更适合我们在面试过程中给出,因为更稳,要是画详图,画错了,那就得不偿失了。以下是三次握手的详图:

再通俗地解释一下三次握手的过程:

  1. 客户端向服务器发起建立连接的请求,发送一个SYN,此处的SYN就表示:我想跟你建立连接.
  2. 服务器大概率是会同意请求的(除非客户端太多了,负载极高,服务器无法响应),服务器收到SYN之后,会返回ACK(确认应答),表示收到!同时,服务器还会返回SYN,这个SYN的意思就是,我接受你的连接.
  3. 最后,客户端再给服务器返回一个ACK,表示:我也知道你同意我的请求了.

建立连接(三次握手)的意义

1)投石问路,确认当前通信路径是否畅通

例如地铁,每天早上会空车跑一趟,就是为了验证,这个路线是否畅通,是否存在一些问题。

因此,三次握手,对于可靠传输也是有意义的,但是相对于确认应答以及超时重传,起到的作用就稍逊一筹了。

2)验证通信双方,发送能力和接收能力是否正常

比如,张三和李四要开黑打游戏了。通常,在组队队伍的时候,双方都不知道自己的麦克风和听筒是否能正常工作,都先进行确认,这个过程其实就是三次握手,如下图:

上图中,张三扮演的就是客户端的角色,李四扮演服务器的角色。经过上述过程,张三和李四就够确认各自的麦克风和听筒都能正常工作了。此处,麦克风就对应发送能力,听筒就对应接收能力。

理解了上述图中的过程,我们就能清晰地理解,为什么是三次握手,而不是两次或四次握手:

  • 四次握手显然是可以的,但是没必要。
  • 如果是两次,一定是不行的。因为两次握手,李四(服务器)这边无法确认自己的发送能力是否正常,也无法确认张三(客户端)的接收能力是否正常。

3)三次握手的过程中,通信双方也会协商一些必要的参数

这些参数往往是以“选项”部分来体现的,前面说过,选项的范围是0~40字节。

其中有一个参数是很关键的,即TCP通信的序号:

  • 客户端在发送SYN报文时会包含一个随机生成的序列号(ISN),用于标识客户端发送数据的起始位置。
  • 服务器在回复ACK报文时会确认客户端的序列号,并生成自己的序列号(SSN),用于标识服务器发送数据的起始位置。

这样设计的原因,是为了避免出现一个情况,“前朝的剑,斩本朝的官”。具体过程如下:

  • 第一次连接的过程中,客户端传输的一个数据包,在路上堵车了,迟迟没有发送到对端。
  • 等到到了对端的时候,此时已经不是第一次连接了,而是新的连接了。
  • 此时,这份数据就应该被丢弃。
  • 由于,数据包是按照 IP + 端口号,来识别对端的。
  • 如果恰好出现,两次连接在同一台主机,都是同一个端口号的情况(这种情况是客户端的概率比较低,但服务器概率就比较大了),此时,再要处理这份数据就不合适了。

要解决这一问题,就是通过序号来区分当前数据是否是本次连接的数据。如果是,才进行处理,否则,就丢弃该数据。

二、断开连接(四次挥手)

连接,本质上就是让通信双方保存对方的信息。每个客户端/服务器,都要保存很多的对端信息。

信息一旦多了,就要用到数据结构。断开连接的本质目的,就是把对端的信息,从数据结构中删除/释放掉。

此处谈到的“四次挥手”,指的是谈恋爱中“和平分手”的这种情况。而单方面分手的情况,四次挥手不一定适用,这里会有其他方式来断开连接,因此,不意味着断开连接一定是四次挥手。

此处,又要介绍一个标志位,FIN,FIN => finish(结束)。即六位标志位中的第六位,用于请求关闭连接。

与三次握手不同的是,四次挥手,不一定非得是客户端先发FIN,服务器也可能先发FIN。

  • 调用socket.close(),会触发FIN
  • 进程直接结束,也会触发FIN

四次挥手的过程简图如下(客户端先发起关闭连接请求):

从上图可以看出,四次握手就是通信双方各自给对方发一个FIN,且各自给对方反馈一个ACK。

而且,这里的过程和刚开始的“四次握手”的过程非常相似。那四次挥手能否像三次握手一样,将中间的两次交互(ACK和FIN)合二为一呢?

  • 在标准的TCP四次挥手过程中,ACK和FIN通常是分开发送的。
  • 然而,在某些情况下,确实可能会发生ACK和FIN合并发送的情况,因为在理论上,ACK和FIN可以在同一数据包中发送。这种情况主要依赖于延迟应答和捎带应答机制(后面再说)。

如果实际通信过程中,ACK和第二个FIN时间间隔比较长,此时就无法进行合并了。以下是四次挥手的详图:

三、连接管理过程中涉及到的TCP状态转换

在前面三次握手和四次挥手的详图中,每进行一次握手或挥手,图中都有一个对应的状态。

TCP状态和之前多线程中谈到的“线程”状态,是类似的概念。TCP会根据收到的数据包以及协议规定的状态转换规则来更新连接状态:

  1. 分析收到的数据包: TCP协议栈会分析收到的数据包的头部信息,包括源端口、目标端口、标志位等,以确定数据包所处的状态以及下一步的操作。

  2. 执行状态转换规则: TCP协议定义了一系列状态转换规则,规定了在收到不同类型的数据包时应该如何更新连接状态。根据这些规则,TCP协议栈会根据收到的数据包来进行状态转换。

根据这些状态,TCP就能确定当前应该干什么,对于确保网络通信的可靠性和有效性至关重要。

这个图看起来非常复杂,实际上也真的很复杂,但是只需要关注几个比较重要的状态即可。 

先来看三次握手中比较重要的状态:

客户端还未发起SYN前,服务器处于LISTEN状态,表示服务器这边创建好 serverSocket 了,并且绑定端口号完成(手机开机了,信号良好,随时可以接听电话)

运行之前写的TCP回显服务器程序,通过命令行输入 netstat -ano | findstr 9090  这条命令(在这个命令中,netstat -ano 的作用是显示所有网络连接及其相关的进程ID(PID),而 findstr 9090 则是在输出包含端口号9090的所有进程),查看当前的状态:

ESTABLISHED 已确立的。表示客户端和服务器连接已经建立完毕(有人给我打电话,我接通了,此时我们就可以聊天了)

运行之前写的TCP回显客户端程序(可以运行多次),再次通过这个命令查看当前状态:

此处由于客户端和服务器在同一个机器上运行,因此客户端和服务器都会查出来。

再看四次挥手中比较重要的状态:

谁被动断开连接,谁进入 CLOSE_WAIT 状态。这个状态表示:

  • 接收方已被通知对方不再发送数据,但它仍然可以发送数据给对方。接收方需要执行应用层的关闭操作,并发送一个FIN包来关闭剩余的方向,即关闭对发起方的数据传输。 

CLOSE_WAIT 状态不太容易观察到,代码中会比较快速地关闭socket,这个时候,状态就已经从 CLOSE_WAIT -> LAST_ACK 了。但是如果服务器/客户端出现大量 CLOSE_WAIT  意味着代码可能出现bug了,比如忘记关闭 socket。

谁主动断开连接,谁进入 TIME_WAIT 状态:

  • 表示本端给对端发起FIN之后,对端也给我发FIN了,此时本端进入 TIME_WAIT 状态。这给最后一个ACK的重传留有一定的时间。

TIME_WAIT 状态在Windows上也不太容易观察。它存在的意义,主要是防止最后一个ACK丢失。具体的:

  • 在四次挥手的过程中,会涉及到确认应答和超时重传,如果没有收到ACK就视为丢包,此时就会重传FIN。
  • 进入TIME_WAIT 状态的这一方,如果在重传FIN这个环节,把TCP连接关闭了,保存的对端信息也随之释放了。此时意味着重传FIN的这一方就无法被返回ACK了。

此处 TIME_WAIT 也不是无休止的等待,通常等2MSL(MSL是TCP协议中的一个参数,表示数据段在网络中的最大存活时间,通常以秒为单位。MSL的值取决于具体的实现和环境,但通常为30秒到2分钟之间)

进入TIME_WAIT 状态的这一方等这么长时间,如果对方还没有重传,就说明不会再重传了。同时也确保了对方收到了最后的ACK确认。

至此,TCP协议的连接管理机制就介绍完了,并且前面还介绍了确认应答和超时重传机制,这三个机制都是保证TCP协议可靠传输的重要机制。连接管理机制总结如下:

三次握手(Three-way Handshake):

  1. 第一步(Client -> Server): 客户端发送一个带有SYN标志的数据包给服务器,表示请求建立连接,并选择一个初始序列号(ISN)。
  2. 第二步(Server -> Client): 服务器收到客户端的SYN包后,回复一个带有SYN和ACK标志的数据包给客户端,表示同意建立连接,并确认客户端的ISN,同时选择自己的ISN。
  3. 第三步(Client -> Server): 客户端收到服务器的确认后,再次发送一个带有ACK标志的数据包给服务器,确认服务器的ISN。此时,连接已建立,双方可以开始传输数据。

四次挥手(Four-way Handshake):

  1. 第一步(Client -> Server): 客户端发送一个带有FIN标志的数据包给服务器,表示不再发送数据,但仍可以接收数据。
  2. 第二步(Server -> Client): 服务器收到客户端的FIN包后,回复一个带有ACK标志的数据包给客户端,确认收到了关闭请求。
  3. 第三步(Server -> Client): 服务器发送一个带有FIN标志的数据包给客户端,表示服务器也不再发送数据,但仍可以接收数据。
  4. 第四步(Client -> Server): 客户端收到服务器的关闭请求后,回复一个带有ACK标志的数据包给服务器,确认收到了关闭请求。此时,连接终止。

通过三次握手,客户端和服务器建立起了可靠的连接;而通过四次挥手,双方安全地终止了连接,确保数据的可靠传输。


网站公告

今日签到

点亮在社区的每一天
去签到