【网络】传输层——TCP(滑动窗口流量控制拥塞控制延迟应答捎带应答)

🐱作者:一只大喵咪1201
🐱专栏:《网络》
🔥格言:你只管努力,剩下的交给时间!
图

上篇文章对TCP可靠性机制讲解了一部分,这篇文章接着继续讲解。

🎨滑动窗口

在上篇文章中,本喵讲解了TCP的确认应答机制:

图
如上图所示,主机A每发送一个数据段,主机B都要给一个ACK确认应答, 主机A收到ACK后再发送下一个数据段。

这样做有一个比较大的缺点, 就是性能较差,数据段和数据段之间的发送就变成了串行的了,尤其是数据往返的时间较长的时候,效率更低。

为了提高效率,采用一次发送多条数据的方式:
图
如上图所示,假设一个数据段的大小是1000字节,主机A一次性发送四个数据段,主机B一次给主机A四个ACK确认应答。

我们知道,TCP协议中有超时重传机制,如果主机A在一定的时间内没有收到主机B的确认应答,那么就会触发超时重传,再次将刚刚的数据段发送一遍。

  • 所以,数据段被发送出去以后,不能立马清除,需要再保存一段时间,直到收到对端的确认应答,这样是为了以防超时重传时再次发送。

那么在收到确认应答之前这些暂存的数据段是存放在哪里的呢?答案是存放在发送缓冲区中。

图
如上图所示,用户层将数据send到TCP的发送缓冲区,发送缓冲区会存在大量的数据,需要操作系统在合适的时候发出去,由于TCP是面向字节流的,所以势必不会一次性将发送缓冲区中的数据都发出去。

此时就会导致发送缓冲区中的数据有三种不同的状态,同时也将发送缓冲区分成了三部分:

  1. 已经发送并且收到ACK的数据。

如上图序号1所示,这个区域的数据是已经发送了,并且收到了ACK确认应答的数据,说明这些数据对方完全收到了,就没有存在的必要了,所以新数据来了以后会将其覆盖。

  1. 已经发送但是没有收到ACK的数据。

如上图序号2所示,这个区域的数据是已经发送了,但是还没有收到ACK确认应答,说明这部分数据对方可能没有收到,也可能对方的ACK确认应答信号自己没有收到。

当触发超时重传后,这部数据会被再次发送,所以这部分数据不能被覆盖,也不能被清除。

  • 发送缓冲区中,存放已经发送但是没有收到ACK数据的区域就是滑动窗口
  1. 没有发送的数据。

如上如序号3所示,这个区域的数据还没有发送,更谈不上有没有ACK确认应答。


🧵理解滑动窗口

图
如上图所示,内核中的发送缓冲区可以看成是一个char outbuffer[N]数组,存在win_startwin_end两个指针来标识滑动窗口的范围,窗口滑动的本质就是数组下标的更新。

  • 窗口最开始的大小是如何设定的?

图

如上图红色框中所示,在TCP协议的报头中有一个16位的窗口大小,该值就是滑动窗口的大小。

在通信双方进行三次握手建立连接的过程中,接收方将自己的接收能力告诉了发送方(起初是整个接收缓冲区的大小),也就是发送方在发送数据之前就确定了滑动窗口tcp_win的大小。

伪代码:

win_start = 0;
win_end = win_start = tcp_win;

所以最开始,滑动窗口的大小是从发送缓冲区起始位置开始的tcp_win个字节,也就是对方通告给我的接收能力大小。

  • 窗口一定是向右滑动吗?会不会向左滑动?

图

如上图所示,假设现在滑动窗口的大小是4个数据段,也就是4000个字节,主机A先发送一个数据段(1001~2000),当主机B收到并且返回ACK时,其中确认序号是2001

  • 确认序号表示发送端下次发送从这个序号的位置开始发送即可。

此时原本滑动窗口中的第一个数据段就变成了已经发送并且收到ACK的数据,可以被覆盖了,所以滑动窗口继续向右滑动,第一个数据变成了原本的第二个数据段(2001~3000)。

  • 由于数据的发送是从左到右的,数据滑动窗口的滑动方向也是从左到右的,不会向左滑动。
  • 窗口大小会一直不变吗?变的依据是什么?

图

如上图所示,假设发送缓冲区中一个黑色小框是一个数据段(大小是1000字节),在没有收到ACK前,滑动窗口的大小是5个数据段。

图
收到ACK确认应答如上图所示,确认序号是1001,说明0~1000的数据段对方收到了,下次从1001处开始发送,所以滑动窗口的win_start向右滑动一个数据段,指向1001处。

除此之外,由于对方的接收缓冲区应用层没有读数据,再加上又有新数据到来,所以接收能力下降了,应答信号中的16位窗口大小表示对方此时的接收能力是3000,所以此时滑动窗口的大小就要发生变化。

win_end = win_start + tcp_win(3000)得到的就是新滑动窗口的结束位置,此时滑动窗口相比原来变小了。


同样的,也有可能对方在发送这次ACK确认应答的时候,应用层恰好把整个接收缓冲区的数据都读走了,此时接收能力就变大了,确认应答信号中表示接收能力的16位窗口大小的值也比之前要大。

虽然win_start在向右移动,但是win_end = win_start + tcp_win向右移动的更多,所以此时滑动窗口和原来相比变的更大了。

  • 滑动窗口变化大小的依据是对方接收能力的大小
  • 收到的确认应答不是滑动窗口最左边数据的确认应答,而是中间的,或者结尾的,要滑动吗?

图

发送数据的顺序是从左到右的,理论上收到确认应答的顺序也是从左到右的,收到不是最左边数据的确认应答的情况,一定是丢包了。

丢包又有两种情况,一种情况是发送的数据没有丢,对方也收到了,只是对方的确认应答信号丢了,没有发送过来,如下图:

图
这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认。

  • ACK确认序号:该序号前的所有数据全部都收到了,下次从该序号处开始发送。

如上图所示的情况中,即使确认序号为1001的ACK丢了,但是确认序号为2001的ACK没有丢。收到2001的ACK后就知道1~1000的数据也收到了,滑动窗口可以直接向右移动两个数据段。

第二种情况就是发送的数据丢了,对方没有收到,如下图:

图
如上图所示的情况中,1~1000的数据段丢失之后,发送端会一直收到确认序号为1001 的ACK,就像是在提醒发送端 “我想要的是1001” 一样。

如果主机A连续三次收到了同样一个确认序号是1001的应答,就会将包含1001的数据段(1001~2000)重新发送。

这个时候收到了1001~2000的数据段之后,返回ACK的确认序号就是7001了。因为2001 - 7000其实之前就已经收到了,并且被放到了接收端操作系统的内核接收缓冲区中。

  • 这种重传机制被称为高速重发控制
  • 也被叫做快重传
  • 滑动窗口必须要滑动吗?会不会不动?大小会不会变为0?
  • 滑动窗口是否滑动的依据ACK中的确认序号。

当发生丢包等情况时,滑动窗口是不会发生滑动的,因为无法确定对方是否收到了发生的数据,此时就会保持不动,等待下一步策略执行。

当对方的接收缓冲区满了,并且应用层没有读走数据时,此时接收能力就是0,所以ACK确认应答中的16为窗口大小也是0,此时发生方滑动窗口大小就会变成0。

  • 一直向右滑动吗?如果空间不够了怎么办?

图
滑动窗口如果一直向右滑动,当发生缓冲区的空间不够时,滑动窗口会不会越界呢?答案是不会的。

图
因为发送缓冲区被操作系统组织成了环状结构,所以滑动窗口无论怎么滑动都不会越界。

🎨流量控制

接收端处理数据的速度是有限的,如果发送端发的太快,导致接收端的缓冲区被打满,这个时候如果发送端继续发送,就会造成丢包,继而引起超时重传等等一系列连锁反应。

  • TCP支持根据接收端的接收能力来决定发送端的发送速度。这个机制就叫做流量控制(Flow Control)

和滑动窗口中发送端知道接收端接收能力一样,接收端将自己可以接收的缓冲区大小放入TCP首部中的16位窗口大小中,再通过ACK端告诉发送端自己的接收能力。

  • 窗口大小字段越大,说明网络的吞吐量越高。

图

如上图所示,主机A先发送了一个数据段,得到的ACK中,窗口大小是3000,表示主机B有三个数据段的接收能力,主机A下次发送数据时,调整为一次发送三个数据段。

由于应用层读取数据缓慢等原因,接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端,发送端接受到这个窗口之后,就会减慢自己的发送速度。

如果接收端缓冲区满了,就会将窗口置为0,这时发送方不再发送数据,接收方也不再有ACK确认应答了。

  • 发送端定期发送一个窗口探测数据段,让接收端把窗口大小告诉发送端,一但窗口值不再是0了,发送端就可以额继续发送数据。
  • 否则通信就会暂停在这里了。

图

16位变量的最大值65535,那么TCP窗口最大就是65535字节么?

实际上,TCP首部40字节选项中还包含了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位。有兴趣的小伙伴自行研究首部中的选项。


在TCP首部的6个标志位中有一个PSH标志位,该标志位的作用是催促接收端应用层尽快从接收缓冲区中将数据读走。

当接收端的接收能力快为0的时候,在发送数据的时候可以将PSH置一,接收方收到后发现PSH为一,就会尽快将接收缓冲区中的数据拿走,好尽快提高接收能力。

🎨拥塞控制

图
如上图所示,此时客户端要发送1000个数据段,服务端接收到以后返回了ACK确认应答,客户端根据确认序号发现丢了两个数据段,直接使用快重传或者超时重传机制重新发送这两个数据段即可。

但是如果有999个数据段丢了,服务端只收到一个数据段,那么此时就是网络出问题了,所以导致大量数据没有发出去。

  • 少量的丢包,仅仅是触发超时重传,大量的丢包,就认为网络拥塞。
  • 客户端对于这999个发送失败的数据该如何处理呢?

如果也进行重传,那么就会让已经出现问题的网络雪上加霜。并且一个局域网中不止你一个客户端在发数据,如果都采用这种重传方式,那么整个网络中就会存在大量拥塞的数据,使网络问题更加严重。

  • 遇到网络拥塞时,不能使用超时重传机制,而应该使用拥塞控制的策略。

先不管拥塞控制是什么,从TCP有这一机制就可以看出,TCP的可靠性不仅仅考虑了双方主机的问题,还考虑了路上网络的问题!。

  • TCP引入慢启动机制来实现拥塞控制:
  • 当发送网络拥塞后,先发少量的数据探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。

图
如上图所示,当网络发生拥塞时,主机A先发送一个数据段探探路,如果收到这个数据段的ACK,再将发送数据段个数增加。

  • 此处引入一个拥塞窗口的概念。
  • 表示会产生网络拥塞的数据量。

在网络拥塞后,第一次发送数据时,将拥塞窗口的大小设置为1,即一个数据段(1000)的大小,每次收到一个ACK应答,拥塞窗口加1。

再次发送时按照拥塞窗口的大小来发,这一就会导致发送数据段的个数按照指数级来增长,每次都是前一次的二倍。

之所以称这种方式是慢启动,是因为最开始只发送一个数据段,开始数据量的增长确实慢,但是这是指数级增长,后面数据量的增长就会越来越快。

  • 慢启动的方式,可以让发送方发送的数据量快速恢复到正常水平。
  • 这种快速恢复提高了网络通信的效率。

但是不能让这种增长速度不断增加下去,否则就导致非常恐怖的数据量,所以不能使拥塞窗口单纯的加倍。

  • 再引入一个概念:慢启动的阈值

当以指数增长的拥塞窗口大小大于这个阈值的时候,拥塞窗口不再按照指数级增长,而是按照线性方式增长。

图
如上图所示便是拥塞窗口随传输轮次变化的示意图。

网络拥塞控制机制触发,势必是因为已经发生了网络拥塞,当上一次网络拥塞发生时,阈值大小为16,当发送方以慢启动方式开始发送数据后,拥塞窗口按照指数级增长到16后变成了线性增长。

拥塞窗口线性增长到24以后,再次发生了网络拥塞,因为在这个过程中,发送的数据量也在不断增加。此时将阈值更新为24的一半12。

然后发送方再以慢启动的方式发送数据,拥塞窗口变成12以后再线性增长,直到发送网络拥塞,再次更新阈值。

如此反复,不断更新阈值和拥塞窗口的最大值,以便试探出当前网络状况下效率最高的数据传输量(阈值和拥塞窗口最大值不再变化)。


如果说在拥塞控制的过程中,网络状况恢复了,那么拥塞窗口就会一直增长下去,发送数据量也在增长,直到当前良好网络状况极限吗?

接收方也是有接收能力限制的,就算网络情况再好,发送方也不以超出接收方接收能力数据量来通信。

  • 发送方在每次发送数据的时候,会将拥塞窗口的大小和接收端反馈接受能力大小作比较,取较小值作为实际发送的数据量。
  • 滑动窗口大小 = min(拥塞窗口大小,对方接收能力大小)。

所以在网络状况良好的情况下,发送方的滑动窗口大小取决于接收方的接收能力,在网络拥塞的情况下,发送方的滑动窗口大小取决于拥塞窗口的大小。


当TCP开始启动的时候,慢启动的阈值等于滑动窗口的最大值,如果网络状况良好,那么拥塞窗口在增加到大于接收方的接收能力后变不再增加。

如果发送了网络拥塞后,慢启动阈值就会变成原来的一半,同时拥塞窗口置回1(最小值),逐渐找到最合适的拥塞窗口值和阈值。

  • 当TCP通信开始后,网络吞吐量会逐渐上升。
  • 随着网络发生拥堵,吞吐量会立刻下降。

🎨延迟应答

假设接收端缓冲区大小为1MB,一次收到了500KB的数据,如果立刻应答,返回的窗口大小就是500KB。

但实际上可能接收端处理接收缓冲区中数据的速度很快, 10ms之内就把500KB数据从缓冲区消费掉了,并且接收端处理速度还远没有达到自己的极限,即使窗口再放大一些,也能处理过来。

如果接收端稍微等一会再应答,比如等待200ms再应答,那么这个时候返回的窗口大小就是1MB了。

  • 窗口越大,网络吞吐量就越大,传输效率就越高。
  • 我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。

所有的数据报都采用延迟应答的方案吗?肯定不是。

常用的方案有两种:

  • 数量限制:每隔N个包就应答一次。
  • 时间限制:超过最大延迟时间就应答一次。

具体的数量和超时时间,依操作系统不同也有差异。一般N取2,超时时间取200ms。

图
如上图所示,延迟应答的最终表现,就是隔几个数据段确认应答一次。

🎨捎带应答

本喵再讲解协议格式的时候,说协议首部中有两个序号是为了实现全双工,也就是让应答和数据在一个数据段内。

即使有延迟应答,但是很多情况下,通信双方 “一发一收” 的,虽然是收到多个数据段应答一次,但是应答终究还是只有应答,也就是只有确认序号和ACK标志位,没有数据。

为了通信效率更高,完全可以将确认应答和接收端要发送的数据放在一个数据段中发送出去,发送端收到后既可以知道接收端收到了自己数据,又收到了接收端发送的数据。

图
如上图所示,确认应答信号中的确认序号和ACK标志位,坐着数据的顺风车就发送出去了,也就是在通信的过程中,确认应答就被捎带给对方了。

🎨面向字节流

在学C语言文件操作的时候,就听到过面向字节流,在学C++的时候同样也听到过面向字节流,在UDP和TCP的学习中,更是多次见到面向字节流,那么面向字节流到底是什么?

创建一个TCP的socket时,操作系统会同时在内核中创建一个发送缓冲区 和一个接收缓冲区。

图
应用层在调用write或者send时,数据会先写入发送缓冲区中,如果发送的字节数太长,会被拆分成多个TCP的数据包发出。

如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去。

接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区,然后应用层可以调用read或者recv从接收缓冲区拿数据。

  • 由于缓冲区的存在,TCP程序的读和写不需要一一匹配,如:
  • 写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一个字节。
  • 读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read100个字节,也可以一次read一个字节,重复100次。

简而言之就是,应用层和TCP层是完全独立的,应用层写的时候不用考虑TCP层的缓冲区是否满,应用层读的时候,也不需要考虑缓冲区中的数据是怎么样的,直接读就可以。

  • 至于应用层写入或者读取的是否是一个完整的报文,TCP层的缓冲区无法保证,需要由用户层自己处理。

与面向字节流相对的就是面向用户数据报的UDP,UDP协议用户层写入就是一个完整的报文然后给到UDP层,UDP层并不会缓存,而且增加相应的首部后直接发送出去,发送的上一个完整的数据段

接收方应用层读取的时候,从接收缓存区中读取到的内容也是一个完整的数据段,不能分多次读。

粘包问题:

  • 首先要明确,粘包问题中的 “包”,是指的应用层的数据包。

在TCP的协议头中,没有如同UDP一样的 “报文长度” 这样的字段,但是有一个32位序号的字段。站在传输层的角度,TCP是一个一个数据段过来的,按照序号排好序放在缓冲区中。

站在应用层的角度,看到的只是一串连续的字节数据,那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包,此时应用层在读取数据的时候就会产生粘包问题,可能读取的不是一个完整报文,也可能是一个半报文等等情况。

解决这个问题,归根到底就是要明确两个包之间的边界

在之前的文章协议定制中,采用的是TCP协议,本喵在应用层读取接收缓冲区数据的时候,通过用户层代码来保证每次读取到的是一个完整报文。

  • 每个报文都有一个报头,报头中的内容就是有效载荷的长度,这是由本喵定义的用户层协议。

对于UDP协议来说,就不存在粘包问题:

  • 如果还没有上层交付数据,UDP的报文长度仍然在(在首部中)。同时,UDP是一个一个把数据报交付给应用层就有很明确的数据边界。
  • 站在应用层的站在应用层的角度,使用UDP的时候,要么收到完整的UDP报文,要么不收,不会出现"半个"的情况。

🎨TCP小结

TCP异常情况:

  • 进程终止:进程终止会释放文件描述符,仍然可以发送FIN,和正常关闭没有什么区别。

一个进程终止后,操作系统会释放这个进程的所有资源,包括文件描述符表,会自动调用close关闭对应的文件,当TCP套接字被关闭时,同样会发起四次挥手请求,和正常关闭没有区别。

  • 机器重启:和进程终止的情况相同。

我们平时在关机的时候,会提示有什么什么进程没有结束,要我们强制结束,这个时候也是在终止进程,也会发起四次挥手请求。

  • 机器掉电/网线断开:此时就是一种真正异常情况。

发送端来不及发起四次挥手请求,接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了, 就会进行RST

图
如上图所示,首部的六个标志位中有一个RST标志位,该标志位是用来请求重连的。当发送端在触发超时重发机制后,仍然无法将数据发送到对方,就会发送一个带有RST的数据段请求,请求和对方重新发起三次握手建立连接。


即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在,如果对方不在,也会把连接释放。

另外,应用层的某些协议,也有一些这样的检测机制。例如HTTP长连接中,也会定期检测对方的状态,例如QQ,在QQ断线之后,也会定期尝试重新连接。


紧急指针:

我们知道,TCP协议中缓冲区的数据是按照顺序发送的,接收缓冲区也是按照顺序来接收的,但是如果我们想让某个数据插队呢?让这个数据提前被接收端处理,而不是按照顺序来,此时就用到了紧急指针

图
如上图所示,六个标志位中的URG表示紧急指针是否有效,16位紧急指针指向数据段中具体的某个数。

图
我们知道缓冲区本质上就是一个char类型的数组,如上图所示,而紧急指针是一个16位的变量,所以它也是一个值,范围是0~65535,这个值其实就是缓冲区中有效载荷的偏移量。

  • 一个紧急指针只能指向缓存区中一个数据。

当发送端想让某个数据插队时,就将URG标志位置一,然后让16位紧急指针指向这个紧急数据,再将数据段发送出去。

当接收端收到数据段后,发现URG置一了,说明有紧急指针,有数据需要优先处理,然后再去16位紧急指针字段中拿到紧急数据的位置,然后先读取这个紧急数据。

图
在之前我们使用send以及recv的时候,最后一个参数是flags,之前我们都是设为0的,如果将其设置为MSG_OOB就表示发送或者接收的数据中存在紧急指针,也就是将URG标志位置一了。

在发送或者接收的时候,需要调用第二个参数是msg结构体指针的系统调用,这个结构体中包含紧急指针的位置,也就是16为紧急指针位段。

紧急指针的应用场景非常少,一般应用在紧急获取对端状态的场景,比如说客户端给服务端发送了很多条TCP请求,服务端都没有给回应答,客户端就可以发一个紧急数据确认服务端的状态。

TCP机制总结:

TCP非常的复杂,有众多的机制来保证它的可靠性和提高性能。

保证可靠性机制:校验和,序列号,确认应答,超时重传,连接管理,流量控制,拥塞控制。

提高性能的机制:滑动窗口,快速重传,延迟应答,捎带应答。

其他机制:超时重传定时器,保活定时器,TIME_WAIT定时器。

常见的基于TCP的应用协议:HTTP,HTTPS,SSH,Telient,FTP,SMTP,以及前面本喵自己定制的应用层协议。

🎨理解listen的第二个参数

图
前面本喵在创建TCP套接字的时候,没有讲解listen系统调用的第二个参数backlog,只是说随便设置一个数,不要太大。

  • int backlog表示全连接长度。

图

如上图所示,服务器进程中有一个listen状态的套接字用来监听,还有多个recv后的套接字来进行真正的通信。

系统资源是有限的,当用于通信的套接字数量达到限制以后,系统就无法再维护更多套接字了,新来的已经建立连接的套接字就会由于资源不足而被关闭。

当服务中某个或者几个用于通信的套接字使用完毕后,就会释放出一部分系统资源,此时也没有新的连接到来,那么这部分系统资源就会空闲着。

所以TCP协议维护了一个全连接队列,如上图蓝色部分所示,这个队列中放的是处于等待状态的并且已经完成三次握手建立连接的套接字。

当系统资源不足时,就在全连接队列中等待,当系统资源有空余时,全连接队列中的一个套接字就会被系统维护,进行网络通信。

  • 此时系统资源一空闲出来就会立刻被全连接队列中的套接字使用,不会再出现空闲状态,充分利用了系统资源,提高了效率。
  • listen的第二个参数backlog就是用来指定全连接队列的长度,具体长度等于backlog +1

为什么说backlog值不能太大又不能没有呢?

不能没有的原因就是本喵上面所说的,要让系统资源一有空余就有新的套接字被系统维护,提高系统资源利用率。

不能太大是因为,维护全连接队列也要消耗系统资源,如果全连接队列太长,所耗费的资源完全够系统再维护一个套接字用来通信了,属于是捡了芝麻丢了西瓜的做法。

除此之外,全连接队列太长,里面的套接字等待的时间也会很长,此时又会触发超时重传机制等,进而导致其他问题。


图
将之前写的TCP网络通信代码的backlog值设置为1,也就是将listen的第二个参数设置为1,此时全连接的长度为2。

运行
将服务器运行起来,此时服务器不会accept新的连接,所以当新的连接到来时,被会放入全连接队列中等待,而设置的全连接队列长度是2,所以最多放两个建立连接的套接字。

图
再创建两个Xshell窗口,充当两个服务端,使用telnet工具充当两个客户端与服务器进行连接。

图

使用netstat查看当前主机上和8080端口有关的网络进程,如上图所示。

上面两行中表示两个客户端telnet,它们的端口号分别是4840648410,对端都是服务器,端口号是8080

下面两行中表示服务器和两个客户端telnet,第一行和第三行组成一对通信,通信的端口号是48406 <-> 8080,第二行和第四行组成一对通信,通信的端口号是48410 <-> 8080

此时全连接队列中放的就是端口号为4840648410的两个客户端telnet进程。

  • 全连接队列中的套接字状态是ESTABLISHED,说明是完成了三次握手的,已经和服务器建立了连接,只是服务器没有进行维护进行通信。

图
此时再增加一个客户端,如上图所示,此时一共有3个telnet和服务器建立连接。

图
再次查看网络状态,发现多了两个套接字,上面红色框那行表示服务器,下面红色框表示客户端。

  • 服务器端套接字的状态是SYN_RECV
  • 客户端telnet套接字的状态是ESTABLISHED

图
如上图所示,再三次握手的过程中,服务端第一次收到客户端的SYN请求以后,服务端套接字的状态就变成了SYN_RECV,只有服务端也发送SYN+ACK以后,再收到客户端的ACK以后,服务端才会变成ESTABLISHED,表示连接建立。

而上面过程中,第三个客户端发起连接请求后,服务端套接字停在了SYN_RECV状态,说明服务端已经收到了客户端的三次握手请求,但是此时全连接队列已经满了,服务端没有更多资源来维护这个套接字了,所以不祥客户端发起SYN连接请求。

  • 处于SYN_RECV状态的套接字叫做半连接状态

对于半连接状态的套接字,操作系统同样维护着一个半连接队列,里面放着的是处于SYN_SNETSYN_RECV等半连接状态的套接字。


但是可以看到,客户端telnet的状态是ESTABLISHED,也就是说客户端是认为建立了连接的,但是服务端没有建立,所以这次通信的建立是失败的。

当客户端发送数据的时候,发现服务端没有对应的套接字,就会发起RST,请求重新建立连接。

图
等待一段时间后再次查看网络状态,发现服务器端处于SYN_RECV状态的半连接套接字没有了,而客户端的ESTABLISHED状态的套接字仍然存在。

  • 处于半连接状态的套接字,在一定时间内没有建立连接变成ESTABLISHED状态,操作系统就会将这个套接字释放掉。
  • 半链接队列:用来保存处于SYN_SENTSYN_RECV状态的套接字。

  • 全连接队列:用来保存处于ESTABLISHED状态的套接字,但是应用层没有调用accept获取。

  • 全连接队列满了的时候,就无法继续让当前连接的状态进入ESTABLISH状态了。

🎨总结

TCP协议到此就结束了,可以看到它比起UDP来复杂很多,因为TCP比UDP更加可靠,可靠性的维护是需要付出代价的,增加了序列化,确认应答机制,三次握手四次挥手机制等等很多机制。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS 7 安装MySQL8.0.33

一、查看 CentOS 版本 要查看当前 CentOS 版本&#xff0c;你可以执行以下命令&#xff1a; cat /etc/centos-release 该命令将显示当前 CentOS 的版本信息&#xff0c;例如&#xff1a; CentOS Linux release 7.9.2009 (Core) 在这个示例中&#xff0c;CentOS 版本为 7.…

七麦analysis参数加密分析

文章目录 1. 接口分析2. 断点调式3. 补环境重写加密4. 验证结果 1. 接口分析 目标站点&#xff1a; aHR0cHM6Ly93d3cucWltYWkuY24vcmFuay9tYXJrZXRSYW5r 先刷新网页&#xff0c;请求接口中的analysis参数本次分析的目标 2. 断点调式 打上XHR断点&#xff0c;刷新页面之后断点…

Linux系统中lib64文件夹下包含:动态链接库,静态链接库,内核模块等

lib64 目录对系统稳定运行有重要作用。 目录 lib64文件动态链接库静态链接库内核模块 lib64文件 lib64 文件夹存放主要是可被程序直接加载并使用的 64 位代码模块,包括动态库、静态库、内核模块等,这些文件对程序运行都至关重要。 在 Linux 系统中,lib64 文件夹通常用于存放…

jmeter errstr :“unsupported field type for multipart.FileHeader“

在使用jmeter测试接口的时候&#xff0c;提示errstr :"unsupported field type for multipart.FileHeader"如图所示 这是因为我们 在HTTP信息头管理加content-type参数有问题 直接在HTTP请求中&#xff0c;勾选&#xff1a; use multipart/form-data for POST【中文…

性能分析之MySQL慢查询日志分析(慢查询日志)

一、背景 MySQL的慢查询日志是MySQL提供的一种日志记录,他用来记录在MySQL中响应的时间超过阈值的语句,具体指运行时间超过long_query_time(默认是10秒)值的SQL,会被记录到慢查询日志中。 慢查询日志一般用于性能分析时开启,收集慢SQL然后通过explain进行全面分析,一…

Selenium 测试用例编写

编写Selenium测试用例就是模拟用户在浏览器上的一系列操作&#xff0c;通过脚本来完成自动化测试。 编写测试用例的优势&#xff1a; 开源&#xff0c;免费。 支持多种浏览器 IE&#xff0c;Firefox&#xff0c;Chrome&#xff0c;Safari。 支持多平台 Windows&#xff0c;Li…

MySQL环境安装

文章目录 MySQL环境安装1. 卸载1.1 卸载不要的环境1.2 检查卸载系统安装包 2. 安装2.1 获取mysql官方yum源2.2 安装mysql的yum源2.3 安装mysql服务 3. 登录(1)(2)(3) 4. 配置my.cnf MySQL环境安装 说明&#xff1a; 安装与卸载中&#xff0c;用户全部切换成为root&#xff0c…

==和equals方法之间的区别,hashcode的理解

和equals方法之间的区别 hashcode是什么&#xff1f;有什么作用&#xff1f; Java中Object有一个方法&#xff1a; public native int hashcode(); &#xff08;1&#xff09;hashcode()方法的作用 hashcode()方法主要配合基于散列的集合一起使用&#xff0c;比如HashSet、…

Java数字化智慧工地管理云平台源码(人工智能、物联网、大数据)

智慧工地优势&#xff1a;"智慧工地”将施工企业现场视频管理、建筑起重机械安全监控、现场从业人员管理、物料管理、进度管理、扬尘噪声监测等现场设备有机、高效、科学、规范的结合起来真正实现工程项目业务流与现场各类监控源数据流的有效结合与深度配合&#xff0c;实…

C语言暑假刷题冲刺篇——day3

目录 一、选择题 二、编程题 &#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏✨收录专栏&#xff1a;C语言每日一练✨其他专栏&#xff1a;代码小游戏C语言初阶&#x1f91d;希望作者的文章能对你有…

iOS自定义下拉刷新控件

自定义下拉刷新控件 概述 用了很多的别人的下拉刷新控件&#xff0c;想写一个玩玩&#xff0c;自定义一个在使用的时候也会比较有意思。使应用更加的灵动一些&#xff0c;毕竟谁不喜欢各种动画恰到好处的应用呢。 使用方式如下&#xff1a; tableview.refreshControl XRef…

定量分析计算51单片机复位电路工作原理 怎么计算单片机复位电容和电阻大小

下面画出等效电路图 可以知道单片机内必然有一个电阻RX&#xff0c;为了简化分析&#xff0c;我们假设他是线性电阻&#xff08;不带电容&#xff0c;电感的支路&#xff09; 还有一个基础知识&#xff1a; 电容器的充电放电曲线&#xff1a; 还需要知道电容电压的变化是连续…

Data Abstract for .NET and Delphi Crack

Data Abstract for .NET and Delphi Crack .NET和Delphi的数据摘要是一套或RAD工具&#xff0c;用于在.NET、Delphi和Mono中编写多层解决方案。NET和Delphi的数据摘要是一个套件&#xff0c;包括RemObjects.NET和Delphi版本的数据摘要。RemObjects Data Abstract允许您创建访问…

【C++精华铺】7.C++内存管理

目录 1. C语言动态内存管理 2. C内存管理方式 2.1 new/delete和new T[]/delete[] 2.1.1 操作内置类型 2.1.2 操作自定义类型 2.2 new/delete和new T[]/delete[]的原理 2.2.1 原理 2.2.2 operator new和operator delete 2.2.3 new T[]的特殊处理&#xff08;可以…

docker的资源控制管理——Cgroups

目录 一、对CPU使用率的控制 1.1 CPU 资源控制 1.2 cgroups有四大功能 1.3 设置cpu使用率上限 查看周期限制和cpu配额限制 进行cpu压力测试然后修改每个周期的使用cpu的时间&#xff0c;查看cpu使用率 1.4 设置cpu资源占用比&#xff08;设置多个容器时才有效&#xf…

chatserver服务器开发笔记

chatserver服务器开发笔记 1 chatserver2 开发环境3 编译 1 chatserver 集群聊天服务器和客户端代码&#xff0c;基于muduo、redis、mysql实现。 学习于https://fixbug.ke.qq.com/ 本人已经挂github&#xff1a;https://github.com/ZixinChen-S/chatserver/tree/main 需要该项…

数学建模之“灰色预测”模型

灰色系统分析法在建模中的应用 1、CUMCM2003A SARS的传播问题 2、CUMCM2005A长江水质的评价和预测CUMCM2006A出版社的资源配置 3、CUMCM2006B艾滋病疗法的评价及疗效的预测问题 4、CUMCM2007A 中国人口增长预测 灰色系统的应用范畴大致分为以下几方面: (1&#xff09;灰色关…

小航助学GESP_C++一级模拟测试卷第2套(含题库答题软件账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSDN博客 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;_程序猿下山的博客-CSD…

Python实战之使用Python进行数据挖掘详解

一、Python数据挖掘 1.1 数据挖掘是什么&#xff1f; 数据挖掘是从大量的、不完全的、有噪声的、模糊的、随机的实际应用数据中&#xff0c;通过算法&#xff0c;找出其中的规律、知识、信息的过程。Python作为一门广泛应用的编程语言&#xff0c;拥有丰富的数据挖掘库&#…

2023年国赛数学建模思路 - 案例:最短时间生产计划安排

文章目录 0 赛题思路1 模型描述2 实例2.1 问题描述2.2 数学模型2.2.1 模型流程2.2.2 符号约定2.2.3 求解模型 2.3 相关代码2.4 模型求解结果 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 最短时…