【传输层协议】TCP协议(上) {TCP协议段格式;确认应答机制;超时重传机制;连接管理机制:三次握手、四次挥手}

news2024/11/15 19:45:42

TCP(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层协议,用于在网络上可靠地传输数据。TCP是互联网协议套件(TCP/IP)中的一个主要协议,它在IP(Internet Protocol)的基础之上提供了可靠的数据传输服务。

TCP协议具有以下特点和功能:

  1. 面向连接:在通信双方进行数据传输之前,需要建立TCP连接,包括三次握手和四次挥手过程。这种面向连接的机制可以确保数据的可靠传输和顺序交付。

  2. 可靠传输:TCP通过序列号、确认应答和超时重传等机制来确保数据的可靠传输。如果发生丢包或数据包错误,TCP可以重新发送数据,直到对方确认接收正常。

  3. 流量控制:TCP通过滑动窗口机制来控制数据的传输速率,避免发送方发送数据速度过快导致接收方无法处理。接收方通过发送窗口大小来告知发送方自身的接收能力。

  4. 拥塞控制:TCP通过拥塞窗口和拥塞避免算法来避免网络拥塞,从而提高网络的稳定性和性能。

  5. 面向字节流:TCP将应用程序交给它发送的数据视为字节流,而不是分割成数据块。TCP会将这个字节流分割成合适大小的数据包,传输到接收方后再重组还原为原始的字节流。

总之,TCP是一种可靠的传输协议,适用于需要数据可靠传输和确保数据完整性的应用场景,如网页访问、文件传输、电子邮件等。它是互联网中最常用的传输层协议之一,为网络通信提供重要的支持。


一、TCP协议段格式

TCP协议段的格式如下:

在这里插入图片描述

  1. 源端口号(16位):标示发送该报文段的进程(或应用)的端口号。

  2. 目的端口号(16位):标示接收该报文段的进程(或应用)的端口号。

  3. 序号(32位):指定发送数据的最后一个字节的编号,确保数据的有序传输。

  4. 确认号(32位):指定期望收到的下一个字节的编号,用于实现可靠的数据传输。

  5. 首部长度(4位):以32位字(即4字节)为单位计算出的TCP首部的长度。由于TCP选项字段的存在,TCP首部的长度是可变的,但通常TCP首部的典型长度为20字节。(范围[5, 15] -> [20, 60])

  6. 保留(6位):保留的6个位用于后续协议的扩展。

  7. 控制位(6位):包含多个控制标志,用于管理TCP连接的建立和断开等过程。

    • URG:紧急指针(urgent pointer)有效。
    • ACK:确认号有效。
    • PSH:接收方应尽快将报文段交给应用层。
    • RST:重置连接。
    • SYN:同步序列号,用于建立连接。
    • FIN:结束连接。
  8. 窗口大小(16位):用于指定发送方可以接收的字节数,实际就是发送方接收缓冲区剩余空间的大小。

  9. 校验和(16位):用于检测TCP报文段(首部+数据)在传输过程中是否发生了错误。

  10. 紧急指针(16位):当URG标志位被设置时,紧急指针指向紧急数据的最后一个字节的序号。

  11. 选项(可变长度):用于传输其他信息,如最大段长、最大窗口大小等。

TCP报文段由首部和数据两部分组成。首部固定部分长度为20字节,加上选项字段后,最大长度可达60字节(15*4)。数据部分则包含要传输的应用层数据。

TCP协议段格式的设计充分考虑了数据传输的可靠性、有序性和流量控制等需求。通过序列号、确认号、窗口大小等机制,TCP能够确保数据在网络中的可靠传输。同时,标志位和选项字段的存在,使得TCP能够灵活地处理各种网络情况,满足不同的应用需求。

注意:不同的协议层对数据包有不同的称谓,在传输层叫做数据段(segment),在网络层叫做数据报 (datagram),在链路层叫做数据帧(frame)


1.1 解包与分用

解包:将报头与有效载荷进行分离

TCP从下层获取到一个TCP报文后,虽然TCP不知道报头的具体长度,但报文的前20个字节是TCP的基本报头,并且这20字节当中涵盖了4位的首部长度。

TCP解包的过程如下:

  1. TCP获取到一个报文后,首先读取报文的前20个字节(固定大小的基本报头)
  2. 并从中提取出4位的首部长度(范围[5, 15] -> [20, 60]),此时便获得了TCP报头的大小(基本报头+选项)
  3. 拿到报头长度之后,就可以直接与有效载荷进行分离了

分用:将有效载荷交付给上层协议

  1. 应用层的每一个网络进程都必须绑定一个端口号。

  2. 提取出TCP报头中的目的端口号,找到对应的应用层进程,进而将有效载荷交给对应的进程进行处理,分用完毕


1.2 TCP协议的6个标志位

TCP协议头部中有6个标志位,它们分别是:

  1. URG(URGent):紧急指针有效位,表示TCP报文中的紧急指针字段有效,用于指示紧急数据,通常配合紧急指针字段一起使用。

  2. ACK(ACKnowledgment):确认号标志位,表示该字段的值是有效的,用于确认收到的数据包序号。凡是应答都要带ACK。

  3. PSH(PuSH):推送标志位,表示接收端应该立即将缓冲区中接收到的数据送交上层应用,而不需要等到缓冲区满或者等待一个时间片。

  4. RST(ReSeT):重置连接标志位,用于中断连接、重新建立连接或者处理异常情况。

  5. SYN(SYNchronization):同步序号标志位,用于建立连接时发起连接请求。

  6. FIN(FINish):结束标志位,用于释放连接时通知对方,表示已经发送完所有数据,即将关闭连接。

这些标志位在TCP头部中用于控制连接的建立、数据传输和连接的释放等各个阶段,以确保数据在传输过程中的可靠性和有序性。

可以说标志位就是TCP报文的类型,包含不同标志位的报文段所对应的功能不同。同一个报文可以包含多个标志位,具有多重功能和身份。

TCP协议的紧急数据(带外数据)

  • 紧急数据又被称为带外数据,是插在正常数据流中进行传输的,长度只有1字节。紧急数据会被高优先级处理。

  • TCP协议中的紧急指针(Urgent Pointer)是一个16位的字段,用于指示紧急数据的位置。当 TCP 报文段中的 URG 标志被设置时,紧急指针字段会被用来指示当前数据中紧急数据的末尾位置。紧急指针的值表示紧急数据相对于序列号的偏移量。

  • 当发送方发送包含紧急数据的 TCP 报文时,在 TCP 头部中设置 URG 标志位,并且指定紧急指针的值,以通知接收方需要尽快处理这部分数据。接收方在收到带有紧急指针的 TCP 报文时,会根据紧急指针指示的位置立即处理紧急数据,而不会等待正常数据的处理。

  • 如何发送带外数据?send和recv函数的最后一个参数传MSG_OOB参数可以发送和读取带外数据。对应的TCP报头中URG标记位被置1,且紧急指针指向紧急数据的末尾位置。

参考文章:

TCP-带外数据(紧急数据)_tcp紧急数据-CSDN博客

tcp中的带外数据 - lypbendlf - 博客园 (cnblogs.com)


1.3 其他问题

TCP报头问什么没有有效载荷的长度?

  • TCP报头中没有具体的字段来表示载荷(payload)的大小。TCP协议本身关注的是可靠传输的机制,对于应用层的具体数据并不关心。

  • 如果需要知道TCP报文段中的载荷大小,应用程序通常需要自己进行协议解析,根据自己数据的格式和协议规定来解析报文段,确定载荷的大小。比如,在HTTP协议中,可以通过解析HTTP头部中的Content-Length字段来获取数据的大小。

TCP网络通信中的“一切皆文件”

具体来说,TCP网络通信中的“一切皆文件”可以从以下几个方面来理解:

  1. 套接字(Socket)作为通信端点:套接字是网络通信中的一个关键抽象,它充当了底层协议与上层应用间通信的端点。开发者可以像操作文件一样打开和关闭套接字,通过套接字发送和接收数据。虽然套接字在内部实现上与文件有很大不同,但这种抽象使得网络通信在编程模型上与文件操作相似。
  2. 数据流的概念:在TCP网络通信中,数据以流的形式传输,数据被视为连续的字节序列。这种数据流的概念与文件操作中的读取和写入文件内容非常相似。开发者可以像从文件中读取数据一样从套接字中接收数据,也可以像写入文件一样通过套接字发送数据。
  3. 套接字的缓冲区:套接字缓冲区是套接字(socket)在发送和接收数据时使用的临时存储区域。套接字缓冲区分为发送缓冲区和接收缓冲区,这与文件的读写缓冲区非常类似。

提示:套接字缓冲区的存在可以提高网络通信性能。通过使用缓冲区,可以将应用程序和网络通信进行解耦,使得应用程序可以继续执行而不需要等待网络通信的完成。除了这些套接字缓冲区还参与流量控制、超时重传、按序接收数据等功能。例如:发送缓冲区还可以充当流量控制的作用,当对端接收缓冲区即将占满或已经占满,TCP协议可以控制减少发送流量,或者干脆停止发送。

TCP协议的缓冲区

在这里插入图片描述

  • tcp是全双工通信,因为每个链接(每个套接字)都有对应的发送和接收缓冲区,收发可以并发执行(但是多线程单独收或发必须串行执行)

  • read, write, send, recv等系统调用只负责应用层和传输层缓冲区之间数据的拷贝,而传输层缓冲区和网络之间数据的收发(发多少,何时发)则完全是由tcp协议负责,所以TCP协议称为传输控制协议。


二、确认应答机制

TCP(Transmission Control Protocol)协议中的确认应答机制是确保数据可靠传输的核心机制之一。这一机制通过一系列步骤和规则,确保发送方发送的数据能够被接收方正确接收,并在必要时进行重传,以保证数据的完整性和顺序性。以下是对TCP协议中确认应答机制的详细解析:

2.1 基本流程

在这里插入图片描述

  1. 发送数据:发送方(如客户端)将数据分割成合适大小的报文段,并附加TCP头部信息(包括序列号、确认号、标志位等),然后发送给接收方(如服务器)。
  2. 接收数据:接收方收到报文段后,会检查TCP头部信息,特别是序列号,以确认数据的顺序和完整性。
  3. 发送确认报文:接收方在成功接收数据后,会向发送方发送一个确认报文(ACK报文),该报文中包含确认号(即期望接收到的下一个数据的序列号)。这个确认号告诉发送方,接收方已经成功接收了哪些数据。
  4. 等待确认:发送方在发送数据后,会等待接收方的确认报文。如果在一定时间内(如超时时间)未收到确认报文,发送方会认为数据可能丢失或损坏,并触发重传机制。

为什么要有确认应答机制呢?

  • 简单来讲,发送数据后收到应答,就能够保证发送数据的可靠性。因此不管是客户端还是服务端都要对接收到的数据进行应答。这样就能够保证两个方向上数据发送的可靠性。
  • 至于应答是否被收到,我们无需关心,也就是不需要再为应答而应答。如果应答丢失大不了重传呗,收到重复的数据去重即可。

2.2 关键要素

在这里插入图片描述

  1. 序列号:TCP为每个字节的数据进行编号,这个编号称为序列号。TCP报头中的序列号就是对应发送数据块的最后一个字节的编号。序列号用于标识发送方发送的数据,接收方通过序列号来确认收到的数据是否完整和有序。
  2. 确认号:接收方在发送确认报文时,会包含一个确认号,该确认号表示接收方期望接收到的下一个数据的序列号,同时也表示确认号之前的数据已经被成功接收。
  3. 超时重传机制:如果发送方在一定时间内未收到接收方的确认报文,它会认为数据可能丢失或损坏,并触发超时重传机制。发送方会重新发送那些未被确认的数据,直到收到接收方的确认报文为止。
  4. 流量控制:TCP还通过窗口大小等机制来实现流量控制,防止发送方发送速度过快导致接收方无法处理。接收方会在确认报文中包含自己的窗口大小信息,告诉发送方自己当前能够接收的数据量。

注意:

  • 初始序列号(ISN):通常初始序列号并不是1而是一个随机数字。随机选择初始序列号可以增加TCP连接的安全性,因为具有随机性的初始序列号可以降低被攻击者猜测序列号的可能性。
  • 有了序号为什么还要有确认序号?共用一个字段空间不行吗?TCP报文大多数情况具有多重身份,既是发送也是应答(捎带应答),如果序号和确认序号共用一个字段空间显然不能满足两个方向上的需求。
  • TCP协议中确认序号并不要求必须与序号严格匹配,而是用来表示期望接收到的下一个序号。确认序号告诉对方接收到了哪个序号之前的数据,并为对方提供一个基准点,以便正确地接收后续的数据。上图中,如果并未收到序号2000的报文(丢失)而收到了序号3000的报文,那么应答报文中的确认序号应该是1001(而不是3001)。这样就能够保证确认序号之前的数据已经被成功接收,同时也允许应答有少量的丢失。

2.3 确认应答机制的作用

  1. 确保数据的可靠传输:通过确认应答机制,TCP能够确保发送方发送的数据能够被接收方正确接收,并在必要时进行重传,从而保证数据的完整性和顺序性。
  2. 提高网络传输效率:通过流量控制等机制,TCP能够避免发送方发送速度过快导致接收方无法处理的情况,从而提高网络传输效率。
  3. 支持全双工通信:TCP连接是全双工的,即双方在同一时间既能发送数据又能接收数据。确认应答机制为这种全双工通信提供了可靠的保障。

综上所述,TCP协议中的确认应答机制是确保数据可靠传输的关键机制之一。它通过序列号、确认号、超时重传机制和流量控制等要素,实现了数据的可靠传输和网络传输效率的提高。


三、超时重传机制

3.1 情况一:发送数据丢失

在这里插入图片描述

  1. 主机A发送数据给B之后, 可能因为网络拥堵等原因, 数据无法到达主机B(丢失或超时);
  2. 如果主机A在一个特定时间间隔内没有收到B发来的确认应答, 就会进行重发;

3.2 情况二:确认应答丢失

在这里插入图片描述

  1. 但是, 主机A未收到B发来的确认应答, 也可能是因为ACK丢失了(丢失或超时)
  2. 因此主机B会收到很多重复数据. 那么TCP协议需要能够识别出哪些包是重复的包, 并且把重复的丢弃掉。
  3. 我们可以通过TCP报头中的序列号来实现去重的功能。

3.3 超时重传时间(RTO)

TCP具有超时重传机制,即当一个数据包没有收到确认回复时,会在一定的时间间隔后进行重传。这个时间间隔被称为超时重传时间(Retransmission TimeOut,简称RTO)。那么如何确定超时重传时间呢?

在这里插入图片描述

最理想的情况下, 找到一个最小的时间, 保证 确认应答一定能在这个时间内返回。但是这个时间的长短, 随着网络环境的不同,是有差异的。

  • 如果超时时间设的太长, 丢包很久才会重传,会影响整体的重传效率;
  • 如果超时时间设的太短, 有可能会频繁发送重复的包;

TCP为了保证无论在任何环境下都能比较高性能的通信会动态计算超时重传时间。

平滑往返时间(SRTT)

往返时延(RTT)是指数据从发送端到接收端并返回发送端所需的时间。

一种简单的方法是取RTT的平均值,例如:第一次RTT为500ms,第二次RTT为800ms,那么第三次发送时,可以将两次RTT取平均得到RTO为650ms。

为了确定超时重传时间和更准确地估计RTT,经典算法引入了「平滑往返时间」(Smoothed round trip time,SRTT):每次测量RTT后,都对SRTT进行更新计算,使其更加平滑和准确。

image

平滑因子α是用于计算平滑往返时间(SRTT)的一个参数,建议取值范围为0.8至0.9。具体而言,当α为0.8时,SRTT的计算公式为80%的原始值加上20%的新采样RTT值。

  • 当α趋近于1时,SRTT会更接近上一次的SRTT值,对新的RTT值的影响较小。这意味着对于短暂的时延变化,SRTT会表现出较低的敏感性。
  • 相反,当α趋近于0时,1-α趋近于1,SRTT会更接近新采样的RTT值,与旧的SRTT值的关系较小。这意味着对于时延的变化,SRTT会表现出更高的敏感性,能够更快速地跟随时延的变化而调整。

通过调整平滑因子α的取值,TCP可以根据网络环境的不同情况来灵活地调整SRTT的计算方式,以适应不同的时延变化。这样可以提高TCP的适应性和性能,使其能够更好地应对网络条件的变化。

基于SRTT,TCP进一步计算RTO的值,通常,RTO的值会略大于连接往返时间。

当然,累计到一定的重传次数,TCP会认为网络或者对端主机出现异常,将强制关闭连接。


四、连接管理机制

TCP(传输控制协议)的连接管理机制主要包括两个核心过程:三次握手建立连接四次挥手断开连接。这两个过程确保了TCP连接的可靠性和数据传输的完整性。

4.1 三次握手建立连接

4.1.1 具体过程

三次握手是TCP协议中用来建立连接的过程,它通过三个步骤来确认通信双方的发送和接收能力是否正常,以及协商一些必要的参数(如初始化序列号等)。具体过程如下:

在这里插入图片描述

  1. 客户端发送SYN报文:
    • 客户端首先向服务器发送一个带有SYN(同步序列编号)标志位的TCP报文段。这个报文段不包含应用层的数据,只包含TCP头部信息,其中会包含客户端的初始序列号(ISN)。
  2. 服务器发送SYN-ACK报文:
    • 服务器收到客户端的SYN报文后,会回复一个带有SYN和ACK(确认应答)标志位的TCP报文段,即SYN-ACK报文。这个报文段不仅确认了客户端的SYN报文(ACK=客户端的ISN+1),还包含了服务器自己的初始序列号(ISN)。
  3. 客户端发送ACK报文:
    • 客户端收到服务器的SYN-ACK报文后,会发送一个带有ACK标志位的TCP报文段作为应答。这个报文段确认了服务器的SYN报文(ACK=服务器的ISN+1),至此三次握手完成,TCP连接成功建立。

三次握手的意义在于:

  • 在正式通信之前双方都至少进行了一次收发,确认通信双方的发送和接收能力是否正常。
  • 协商一些重要的参数,如初始化序列号、窗口大小等。
  • 三次握手可以确保一般情况下握手失败的连接成本是嫁接在client端的:要知道链接是要有维护成本的(如创建和维护链接数据结构),而最后一次应答的可靠性是无法保证的,在三次握手中最后一次应答由客户端发起,随后客户端建立链接(不可靠,可能失败),而服务端只有在接收到最后一次应答后才会建立链接(可靠)。相比服务端,客户端的工作压力小,可以承受握手失败的连接成本。

注意:

  1. 客户端调用connect函数只是要求系统构建一个SYN报文段并发送给服务端,只负责发起三次握手,之后便处于阻塞状态等待三次握手完成。至于三次握手的细节作为系统调用接口的connect函数并不过多参与。握手的过程是双方操作系统自主完成的。
  2. 服务端调用accept函数获取链接,本身并不参与三次握手,只会在三次握手成功后将已经建立好的链接返回。如果底层一直没有建立好链接,accept会一直阻塞。

4.1.2 状态转换

服务端:

  1. [CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态,等待客户端连接
  2. [LISTEN -> SYN_RCVD] 一旦监听到连接请求(SYN),就将该连接放入内核等待队列(半连接队列)中,并向客户端发送SYN确认报文
  3. [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了

客户端:

  1. [CLOSED -> SYN_SENT] 客户端调用connect,发送同步报文段(SYN)
  2. [SYN_SENT -> ESTABLISHED] 接收到服务端发送的SYN+ACK报文段,则进入ESTABLISHED状态,connect调用成功返回,开始读写数据

4.1.3 相关测试

测试例程是之前【socket编程】TCP网络通信模型-CSDN博客中的单执行流版本;

服务端、客户端分别位于两台不同机器上启动,测试效果更好;

测试一:服务器完成socket, bind, listen工作后不进行accept,启动服务端、客户端观察链接情况:

在这里插入图片描述

现象:虽然服务器并未调用accept接收新链接,但是无论在客户端还是服务端链接都已经被成功建立了。

结论:连接建立成功与否和上层有没有accept没有关系,三次握手是双方操作系统自动完成的

测试二:将listen函数的第二个参数backlog设为1,启动服务端、客户端观察链接情况:

在这里插入图片描述

现象:

  • 客户端认为自己的3次链接都建立成功了,状态ESTABLISHED
  • 而在服务端前2次链接建立成功,而最后一次链接并未建立成功,状态是SYN_RECV

4.1.4 全连接和半连接

在TCP协议中,全连接(full connection)和半连接(half connection)通常指的是TCP三次握手建立连接的过程中的不同阶段。

全连接和全连接队列

  • 全连接(full connection):在TCP连接的全连接阶段,指客户端和服务器完成了三次握手过程,建立了完整的双向通信连接。在全连接状态下,客户端和服务器可以同时发送和接收数据,实现双向通信。这是TCP连接建立后的正常工作状态。
  • 全连接队列也称为已完成连接队列(Established Connection Queue),用来存储已经完成三次握手的TCP全连接。当服务器接收到客户端的连接请求,完成三次握手建立连接后,就将该连接放入全连接队列中,可以进行数据交换和传输。全连接队列中的连接处于已建立的完整连接状态,可以直接被服务器进程接收和处理(通过accept系统调用获取)。
  • 全链接队列的最大长度(backlog):listen函数的第二个参数backlog再+1就是全链接队列的最大长度。在上面的测试中我们将backlog设置为1,那么对应全链接队列的最大长度就是2。因此,前2次连接可以建立成功,而第3次却不行。
  • 全连接队列的最大长度需要合理设置,既不能太长也不能太短,以确保服务器的性能和可靠性。全链接队列的维护是需要时间空间消耗的,如果设置太长会导致:资源浪费、访问延迟(资源不足)、系统不稳定等问题;相反,全连接队列的功能是缓存已经建立好的连接,如果设置太短会导致:连接拒绝、连接丢失、服务进程空闲等问题。

半连接和半连接队列

  • 半连接(half connection):在TCP连接的半连接阶段,指在三次握手的某个阶段未完成,连接未完全建立,导致连接状态不完整无法进行数据传输。在网络通信中,半连接状态可能会导致资源浪费或连接异常,因此应该及时处理并确保连接状态完整。
  • 半连接队列用来存储还未完成三次握手的TCP半连接,也称为未完成连接队列、SYN队列。如以上测试,在三次握手的过程中,当服务器接收到客户端的SYN请求后,将其放入半连接队列中等待接收客户端的确认ACK,完成连接的建立。但如果此时全连接队列已满,即使收到了客户端的确认ACK,服务端也会选择忽略,任然保持该连接的半连接状态(SYN_RECV状态)。
  • 半连接不会被长时间维护,如果一段时间后上层应用任然没有接收和处理新链接,使全连接队列长时间占满,那么半链接就会被释放。
  • SYN_RECV半链接存在客户端和服务端链接状态不一致的问题,客户端接收到服务端发来的SYN+ACK报文后就认为连接建立成功,而服务端却因服务器繁忙未能建立连接。此时客户端发送数据可能会收到服务端的RST报文要求重新建立链接,客户端多次尝试最终成功建立连接后才能进行正常的通信。

SYN洪水攻击:SYN洪水攻击利用TCP协议中的三次握手机制,通过向服务器发送大量伪造的SYN请求包,以开放的连接(半连接)淹没服务器,导致服务器资源耗尽,无法正常处理合法的连接请求。实际上就是将服务器的半连接队列占满,使得合法的SYN请求被丢弃或拒绝。


4.2 四次挥手断开连接

4.2.1 具体过程

四次挥手是TCP协议中用来断开连接的过程,它通过四个步骤来确保通信双方都能正常结束数据传输并释放资源。具体过程如下:

在这里插入图片描述

  1. 客户端发送FIN报文:
    • 客户端想要关闭连接时,会向服务器发送一个带有FIN(结束)标志位的TCP报文段,表示客户端没有数据要发送了。
  2. 服务器发送ACK报文:
    • 服务器收到客户端的FIN报文后,会发送一个带有ACK标志位的TCP报文段作为应答,确认收到了客户端的FIN报文。此时,客户端到服务器的连接被关闭,但服务器到客户端的连接仍然保持开启状态,以便服务器可以继续发送数据。
  3. 服务器发送FIN报文:
    • 当服务器也想要关闭连接时,会向客户端发送一个带有FIN标志位的TCP报文段。
  4. 客户端发送ACK报文:
    • 客户端收到服务器的FIN报文后,会发送一个带有ACK标志位的TCP报文段作为应答,确认收到了服务器的FIN报文。至此,四次挥手完成,TCP连接被完全关闭。

四次挥手的意义在于:

四次挥手确保了双方都能够确认彼此已经知道连接要关闭,避免出现数据丢失或者连接被意外关闭的情况。通过四次挥手,TCP连接的关闭过程变得有序且安全。

需要注意的是,四次挥手过程中,任何一方都可以先发起关闭连接的操作,而且中间步骤可能会因为网络延迟或丢包等原因而重复进行(超时重传)。


4.2.2 状态转换

客户端状态转化:

  1. [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,开始等待服务器的结束报文段
  2. [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段,进入TIME_WAIT, 并发出LAST_ACK
  3. [TIME_WAIT -> CLOSED] 客户端要等待2MSL(Max Segment Life, 报文最大生存时间)的时间,才会进入CLOSED状态

服务端状态转化:

  1. [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT
  2. [CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
  3. [LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK,彻底关闭连接

4.2.3 相关测试

测试三:服务端调用accept函数接收到新链接,5s后close(sockfd)关闭链接。服务端是主动断开连接的一方。

在这里插入图片描述

现象:服务端连接变成了FIN_WAIT2状态

接着客户端退出,也断开连接:

在这里插入图片描述

现象:服务端连接变成了TIME_WAIT状态,一段时间后该条连接才彻底消失(断开)

在该条链接处于TIME_WAIT状态期间,尝试重新启动服务器:

在这里插入图片描述

现象:发生bind error,错误原因是address already in use,意味着指定的地址和端口已经被另一个进程使用。


4.2.4 TIME_WAIT状态

  • TCP协议规定,主动关闭连接的一方,在四次挥手完成后要处于TIME_ WAIT状态,等待两个MSL(maximum segment lifetime)的时间后才能回到CLOSED状态自动释放。
  • MSL是一个TCP连接在网络中能够存活的最长时间,通常为2分钟,因此,一般情况下TIME_WAIT状态的等待时间为4分钟,但也有可能被系统参数配置影响(Centos7上默认配置的值是60s),最长不会超过240秒。
  • 可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值
  • 在server/client通信过程中,如果server因为某些原因退出,是主动断开连接的一方,那么server端建立的链接就要进入TIME_WAIT状态,这表示连接没有被彻底断开。此时如果server重新启动,会出现端口号绑定失败的问题。这是因为先前绑定的ip-port仍处于TIME_WAIT状态并未被释放,而端口号无法被两个进程同时绑定。
  • 因为客户端的端口号是由操作系统动态分配的,所以如果是客户端主动断开的链接,其链接状态自然也要变为TIME_WAIT,但是不会影响客户端的再次启动,因为操作系统会自动选取一个未被占用的端口号进行绑定。

为什么要有TIME_WAIT状态?

以下是一些主要原因和目的:

  1. 双向数据包传输完整性:在TIME_WAIT状态下,等待一段时间确保最后一个ACK确认数据包被对端接收,保证双方都已经完全关闭了连接。这可以保证双向数据包的传输完整性,防止数据包在过早关闭连接时丢失。

  2. 防止旧的数据包干扰新连接:TIME_WAIT状态的等待时间能够确保网络中已发送的数据包在连接关闭之后不再出现,避免旧的数据包在网络中延迟到达时被错误处理,干扰了正在建立的新连接。例如:如果连接过早关闭,那么延迟到达的数据包会被误认为连接已经关闭而导致的问题。

  3. 避免连接混淆:TIME_WAIT状态保留关闭连接的状态信息,确保端口不会在短时间内被重复利用,防止连接混淆或冲突。这可以避免因为端口资源被频繁占用而导致的连接问题。

  4. 安全性:TIME_WAIT状态能够保护连接的完整性和安全性,防止连接被非法篡改或重放攻击。确保连接关闭的正确性,提高数据传输的安全性。

解决TIME_WAIT状态引起的服务器bind error问题

因为存在TIME_WAIT状态的连接而导致服务器出现bind error,进而就会导致服务器长时间无法重启。这种情况是很不合理的。

可以通过setsockopt函数设置监听套接字的端口重用选项(SO_REUSEADDR | SO_REUSEPORT)为1,来允许多个应用程序共享相同的端口,避免端口资源被浪费。

int opt = 1;
setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2087640.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于STM32开发的智能恒温系统

目录 引言环境准备工作 硬件准备软件安装与配置系统设计 系统架构硬件连接代码实现 系统初始化温度检测与恒温控制OLED显示与状态提示Wi-Fi通信与远程监控应用场景 家庭环境的智能恒温管理实验室或工业环境的精确温度控制常见问题及解决方案 常见问题解决方案结论 1. 引言 智…

VIVADO自定义 IP封装

简介 本章节主要针对VIVAO 2020.2版本做IP自定义封装,其中涉及到IP寄存器读写配置,自定义接口封装等介绍。 IP封装 IP标准自定义步骤一般有创建工程,封装IP,自定义内容,添加自定义库这4个步骤,下面…

探秘紫白洋桔梗花语:勇气、爱情、希望与清晰的象征解读

在缤纷多彩的花卉世界中,紫白洋桔梗宛如一位神秘而优雅的仙子,悄然绽放着独特的魅力。它那淡雅的色彩与别致的花形,令人一见倾心,而其背后蕴含的丰富花语,更是如同隐藏的宝藏一般,等待着我们去细细探寻与解…

蓝花楹花语探秘:从宁静忧郁到等待爱情的深刻寓意

在时光的长河中,有一种花朵宛如梦幻的精灵,每当它绽放之时,那一片绚烂的蓝紫色便如同璀璨的星空倾洒人间,它就是蓝花楹。蓝花楹那独特的身姿和醉人的色彩,仿佛自带一种神秘的魔力,吸引着无数人驻足凝望。而…

vue3+ts+vite+electron+electron-store+electron-builder打包可安装包

yarn create vite yarn add electron yarn add electron-store yarn add electron-builder 新增main.js、preload.js // main.js const { app, BrowserWindow, ipcMain, globalShortcut } require(electron) const path require(path) let store // 我们将在稍后动态导入 el…

Flink优化之--旁路缓存和异步IO

Apache Flink 是一个开源流处理框架,以其高吞吐量、低延迟和事件驱动的处理能力著称。随着大数据和实时处理需求的不断增加,Flink 在许多行业和应用场景中得到了广泛应用,如金融风控、物联网数据处理、实时数据分析等。然而,随着数…

如何学习Linux性能优化?

你是否也曾跟我一样,看了很多书、学了很多Linux性能工具,但在面对Linux性能问题时,还是束手无策?实际上,性能分析和优化始终是大多数软件工程师的一个痛点。但是,面对难题,我们真的就无解了吗&a…

2.11键盘事件

目录 实验原理 实验代码 实验结果 实验原理 简单、常用的键盘事件是等待按键事件,它由 waitKey 函数来实现。无论是刚开始学习 OpenCV,还是使用 OpenCV 进行开发调试,都可以看到waitKey 函数的身影,然而基础的东西往往容易忽略…

[Java]MyBatis轻松拿下

介绍 在业务开发过程中, 都是使用java程序完成数据库的操作, 目前最主流的技术就是MyBatis MyBatis是一款优秀的 持久层 框架,用于简化JDBC的开发。 官网: https://mybatis.org/mybatis-3/zh/index.htmlmybatis是Apache的一个开源项目iBatis, 2010年迁移到了googl…

Day00_场景题

文章目录 资料项目经历技能清单自我介绍QPS和TPS?如何设计一个排行榜的功能?如何解决大文件上传问题延时任务处理场景如何设计一个秒杀系统?分布式幂等性如何设计?如果你的系统的QPS 突然提升10倍你会怎么设计?如何从零搭建 10 万级QPS 大流量、高并发优惠券系统?高 QPS,…

OpenCV绘图函数(9)填充多边形函数fillPoly()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 填充一个或多个多边形轮廓所包围的区域。 函数 cv::fillPoly 填充由若干个多边形轮廓所包围的区域。该函数可以填充复杂的区域,例如带…

【大模型】GPT系列模型基础

前言:GPT整体上与transformer结构相似,但只用了decoder部分。 目录 1. GPT2. GPT23. GPT34. 知识补充4.1 下游任务实现方式4.2 sparse attention 1. GPT 预训练:无监督,根据前k个词预测下一个词的概率。微调: 有监督&a…

AI嵌入式人工智能开发 --- 【1】初始RKNPU

目录 一、NPU的由来 二、RKNPU介绍 三、RKNPU单核框架 3.1 AHB/AXI 接口 3.2 卷积神经网络加速单元(CNA) 3.3 数据处理单元(Data Processing Unit,DPU) 3.4 平面处理单元(Planar Processing Unit&a…

#驱动开发

内核模块 字符设备驱动 中断、内核定时器 裸机开发和驱动开发的区别? 裸机开发 驱动开发(基于内核) 相同点 都能够控制硬件(本质:操作寄存器) 不同点 用C语言给对应的地址里面写值 按照一定的框架格式…

【DSP+FPGA】基于DSP+FPGA XC7K325T与TMS320C6678的通用信号处理平台

DSP FPGA 协同处理架构板载 1 个TMS320C6678 多核DSP处理节点板载 1 片 XC7K325T FPGA处理节点板载 1 个FMC 接口板载4路SFP光纤接口FPGA 与 DSP 之间采用高速Rapid IO互联 基于FPGA与DSP协同处理架构的通用高性能实时信号处理平台,该平台采用1片TI的KeyStone系列多…

CSS3 文本效果(text-shadow,box-shadow,white-space等)

一 text-shadow text-shadow 属性是 CSS3 中用于为文本添加阴影效果的工具。它可以增强文本的可读性和视觉吸引力,提供丰富的视觉效果 1 语法 text-shadow: offset-x offset-y blur-radius color;offset-x:阴影相对于文本的水平偏移量。可以是正值&am…

c/c++: function和procedure的区别

https://www.cs.nthu.edu.tw/~ychung/slides/CSC4180/Alfred%20V.%20Aho,%20Monica%20S.%20Lam,%20Ravi%20Sethi,%20Jeffrey%20D.%20Ullman-Compilers%20-%20Principles,%20Techniques,%20and%20Tools-Pearson_Addison%20Wesley%20(2006).pdf 函数与过程的区别,一个…

AI语音识别神器Openai Whisper对中文的支持如何?

文章目录 前言一、资料准备二、Whisper环境搭建第一步:安装whisper第二步:安装ffmpeg 三、Whisper测试总结其他相关 前言 语音识别一直以来都是人工智能领域中一个不容忽视的技术,随着大模型时代的到来,这项技术也发生了质的变化…

解决MAC电脑SVN Android studio不能提交.so文件相关

目录 前言 确认问题原因 修改 SVN 配置文件 验证配置是否生效 其他注意事项 总结 前言 在使用 macOS 进行开发时,可能会遇到通过 SVN 在 Android Studio 中无法提交 .so 文件的问题。这通常是由于 SVN 配置文件中的 global-ignores 设置导致的,…

比特币牛市将至背后

作者:Arthur Hayes 编译:Liam 「此处所表达的任何观点均为作者个人意见,不应作为投资决策依据,也不应被视为参与投资交易的推荐或建议。」 我打破常规,前往南半球滑雪两周,为北半球的暑假画上圆满的句号。我…