当前位置: 首页 > news >正文

【Linux网络编程九】网络原理之TCP协议【传输层】

网络原理之TCP协议

  • 一.TCP:传输控制协议
    • 1.传输数据(发送和接受)
    • 2.控制数据(可靠性保证)
  • 二.TCP协议段格式
    • ①.16位源端口号和16位目的端口号:
    • ②4位首部长度:
    • ③16位窗口大小:
    • ④32位序号:
    • ⑤32位确认序号:
    • ⑥六个标志位
      • RST
  • 三.TCP可靠性/效率策略
    • 1.连接管理(可靠性保证)
    • 2.流量控制(避免丢包/提高效率)
    • 3.滑动窗口(保证可靠性/效率)
      • 3.1丢包问题
      • 3.2移动原则
    • 4.确认应答机制(避免丢包)
      • 4.1捎带应答(提高效率)
      • 4.2延迟应答(提高效率)
    • 5.序号与确认序号(避免乱序)
    • 6.超时重传(避免丢包)
    • 7.拥塞控制(网络可靠性保证)
    • 8.TCP总结
  • 四.TCP三次握手四次挥手
    • 1.理解三次握手四次挥手
    • 2.两个关键函数
    • 3.建立连接时间不同
    • 4.为什么要三次握手四次握手
    • 5.三次握手的状态变化
    • 6.四次挥手的状态变化

一.TCP:传输控制协议

TCP是工作在四层中的第二层,也就是传输层。
对于TCP来说,它是拥有发送缓冲区和接受缓冲区的,

1.传输数据(发送和接受)

在这里插入图片描述

我们对于网络的理解呢,可以这么认为,应用层,它只负责对
客户发上来的数据呢进行协议处理
,诸如把报文和报头分开,诸
如序列反序化,根据协议对数据做处理,处理完之后呢,把数据
再写给操作系统,而它只要把响应写给操作系统,比如写到tcp
的发送缓冲区中,应用层的任务就完了,完了之后至于这个数据
什么时候发,发多少出错了怎么办由tcp协议决定。

那么应用层把数据发送出去,本质其实是将数据拷贝到了TCP的
的发送缓冲区中
,至于数据
真正的发送,其实是把传输层发送缓冲区缓的数据再拷贝到对方
的接收缓冲区里。
宏观上在tcp协议这里我要把我的数据交给对方,它的本质就是
我把我发送缓冲区里的内容,再经过网络拷贝到对方的接收缓冲区中,
只不过这个拷贝工作呢是要通过网络而已

这个也就称之为叫做全双工,其实就相当于就是解释为什么tcp打开同样
一个文件描述符,你既可以向这个文件描述符读,也可以从文件描述
符里写,为什么呢,因为一个文件描述符它配套的是两个我们对应的
缓冲区,那么所以你就可以既读又写了。

2.控制数据(可靠性保证)

所谓的控制的意思就是说应用层,你把数据呢经过write拷贝到了发送缓冲区
里,至于这个发送缓存区里的数据什么时候发,发多少出错了怎么办本质就是
在控制我们如何发送的问题,那么这些工作呢,由tcp协议自主决定,所以我
们把它叫做传输控制协议
用户层调什么write或者是send并不能够把数据真正的发送给对方,真正将数据写到对方,是由操作系统自主决定什么时候发发多少,出错了怎么办,所以tcp协议的控制特点就体现在这里

所以tcp是一个具有接收和发送缓冲区的进行全双工通信的协议

二.TCP协议段格式

在这里插入图片描述

①.16位源端口号和16位目的端口号:

它是用来确定将tcp报文发送给上层具体什么应用的。

②4位首部长度:

它是用来将报头和有效载荷分离的,标准的报头长度位20字节,里面包含一个叫做4位首部长度的字段,它的取值是[0,15],单位是4字节,所以真正的范围是[0,60]字节,将该字段取出来转换位十进制后,减去报头,剩下的就是数据部分,一般选项是忽略的。
(所以分离方法是:先读取前20个字节,读取四位首部长度,把首首长
首部长度转化成十进制再乘以4,最后就是他的整个爆头长度决定有
没有选项,没有选项,剩下就是数据)

③16位窗口大小:

它是用来进行流量控制的,里面填写的是本端tcp的接受缓冲区中剩余空间的大小。

④32位序号:

它是记录着每个报文的序号,用来保证报文不乱序,每个发送出去的报文都有唯一的编号,这样到达对端,就可以排序实现按顺序达到

⑤32位确认序号:

它是每次发送报文的应答报文的序号,是在发送报文的序号基础上加1。用来确保发送的报文已经接收


⑥六个标志位

标志位存在的意义:,区分tcp报文的类型!!!
在谈六个标志位之前我们需要理解一个概念:在tcp双方通信的过程中,发送和接受的数据都是完整的数据,也就是完整的报文(报头+有效载荷)
在这里插入图片描述

我们双方在通信时有人说发数据,当你看到发数据时,你要想到这里可不仅仅数据,它里面一定要涵盖tcp报头加数据,当你说确认应答时,它里面至少要有个tcp报头。

TCP在通信的过程中,需要先建立链接,然后正常的数据通信,最后断开连接,这三个过程都需要进行通信,也就是发送tcp报文。
所以tcp收到的报文一定是有各种“类型”的,不同的类型的报文,决定了要做不同的动作!!
那么接收方是如何知道,报头的类型是什么呢?就是根据6个标记位来确定的

1.SYN: 表示请求建立连接; 我们把携带SYN标识的称为同步报文段,就是tcp要请求建立连接报文
2.ACK: 确认号是否有效,表示的应答报文。
3.FIN: 通知对方, 本端要关闭了,表示的关闭报文
4.PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走,就是催促对方尽快把缓出去的数据尽快取走。
5.RST: 对方要求重新建立连接;,因为某些异常情况,需要对方重新建立连接
6.URG: 紧急指针是否有效

RST

tcp采用的策略是三次握手,四次挥手。也就是建立链接时需要三次握手才能成功。什么意思呢?
client发送syn报文(一次握手)
server端进行应答:ack+syn报文(2次握手),
client再应答:ack报文,这时只要client将ack报文发送出去,就表示连接成功了。而对于server它是必须先接受到ack后,才能确认连接成功。所以这里面就存在一个时间差,客户端和服务器对于连接是否建立成功存在不一致性,如果最后一个ack丢失了,client端链接建立成功,内核管理链接对象。server却没有建立成功,就存在链接建立异常情况,所以需要发送RST报文,要求重新建立连接。

所以rst标记位呢,它主要是用来进行应对链接异常的情况一旦出问题,我们直接链接重置

三.TCP可靠性/效率策略

1.连接管理(可靠性保证)

我们要理解链接在系统中是什么意思:链接也是一个结构体对象,在内核中需要被管理,需要消耗资源,所以维护链接也需要成本的。

【先描述再组织】在我们看来,客户端和服务器双方建立好链接的本质是我们双方的操作系统内,为了维护链接也要
为链接创建对应的struct结构体,这个链接里面呢,它会包含该链接所对应的属性,比如链接对应的起始序号是什么,确认序号是什么,链接建立的时间是什么,客户端相关的ip端口号是什么,当前我们发送缓冲区在什么位置等等
建立好链接时,我们对应的客户端和服务端一定在内核当中要维护对应的结构对象,双方在进行通信时,我们就可以通过链接来管理双方通信的这个信道。.


要注意每一个连接都有一对接收缓冲区,每个连接互不影响!

2.流量控制(避免丢包/提高效率)

双方通信时,一旦对方接收缓冲区快满了,而客户端还
在一直发,最后就直接导致接收缓冲区没空间了,进
而导致我们对应的数据出现大面积丢包的情况,丢包是不可靠的一种,所以我们的客户端和服务器在通信的时候我们必须想办法,当我们的服务器的接受缓冲区快满了,接收能力已经没有或者很少的时候,我们要让客户端发慢一点点,或者干脆不发了。

这种由我们的客户端向服务端发消息,通过控制发送数据的速度来让
对方来能来得及接收,从而规避大面积丢包这样的情况,我们称之为
叫做流量控制。

知识点1:那么流量控制是如何实现的呢?怎么让客户端或者服务器端发送速度慢一点,依据是什么呢?
依据:对于发送方来讲,发送速度是由对方的按受缓冲区中到剩余大小决定的!!


知识点2:那么如何确定对方的接受缓冲区大小呢???
客户端发送数据是发送整个报文(报头+有效载荷),该报头里有个字段就是16位窗口大小,里面记录着接受缓冲区剩余空间大小。客户端将数据发送给服务器时,服务器必须要进行应答,该应答也是一个完整的报文,报头的16位窗口大小里记录着服务器的接受缓冲区剩余空间大小。
所以,双方就可以根据报头里的16位窗口大小互相得知对方的接收缓冲
区剩余空间的大小。所以双方就可以互相进行流量控制。
在这里插入图片描述


知识点3:第一次发送数据时,怎么保证数据量是合理的?
要注意三次握手,也是交换了完整的报文,这个报文里就携带自身的接收缓冲区大小,所以,在发送数据之前,三次握手已经协商好双方的接收能力。


知识点4:通信过程中,如果接收缓冲区满了,发送端就不会再发送数据了,那么发送方如何知道下次什么时候再发送呢?
①方案1:发送端会定期发送一个窗口探测报文,使接收端把窗口大小告诉发送端
②方案2:接收端的滑动窗口一旦更新了,就会立即向发送端发送一个窗口更新通知,告诉发送端窗口大小。
在这里插入图片描述


知识点5:接收端的滑动窗口默认是65536字节64kb,但实际上tcp首部选项中还包含一个扩大因子M,可以将窗口扩大。就是原来的大小向左移M位。

3.滑动窗口(保证可靠性/效率)

在这里插入图片描述

在通信过程中,可能会存在许多已经发送的但还没有接收到应答的报文,这种报文不确定是否丢失,所以发送端就需要暂时保存一份该报文。那么这些报文保存在哪里呢?

发送缓冲区中本来不就有该报文么,不需要保存在哪里,直接在发送缓冲区里操作就可以了。

知识点1:滑动窗口
发送缓冲区本质可以划分为三部分:①已发送已接收应答的报文②可以发送/已发送但未接收到应答的报文③待发送的报文
而滑动窗口就是在发送缓冲区中,表示可以发送/已经发送但没有收到应答的报文区域。
在这里插入图片描述
利用双指针算法,或者双下标,来对滑动窗口进行区域划分。当发送到数据接收到应答了,则start往后移,表示该报文已经接收应答。若将待发送的报文发送出去,没有得到确认,则将end往后移。只有确认应答过的数据, 才能从缓冲区删掉;
【注意】窗口越大, 则网络的吞吐率就越高;


知识点2:滑动窗口的范围大小
滑动窗口表示可以发送的报文范围,目前其实就是对方接收缓冲区剩余大小。至少滑动窗口的大小,不能超过对方接收缓冲区剩余空间的大小,即应答报文的窗口大小。
但最终结论是,滑动窗口的大小是min(对方主机的接收窗口大小,拥塞窗口大小)
因为TCP既考虑了对方的接收能力,又考虑了网络的接收能力,综合决定的,


知识点3:滑动窗口本质是流量控制的底层技术

3.1丢包问题

知识点1:ACK丢了
虽然少量的ACK丢了,但只要我们接收到最新的一个报文的ACK,根据确认序号的意义,表示在该序号之前的所有报文全部收到了,所以是允许少量的ACK丢失的,所以不会影响滑动窗口的移动。可以保证滑动窗口线性的向后更新,不会出现跳跃情况。
在这里插入图片描述
接收到5001的ACK,就表明5001之前的所有报文都已经接收到了。


知识点2:报文丢了【快重传】
当报文丢了,则后续的报文的应答只会发送丢包之前哪个报文的ACK。当连接接收到三个同样的应答时,就会重新发送该报文,当对方接收到该报文时,就会返回最新发送报文的ACK。这种接收三个同样的确认应答时就重传的机制称为快重传
在这里插入图片描述


知识点3:快重传与超时重传区别
已经有了快重传,为什么还要超时重传?
快重传是有条件的,它是用来提高效率的,而超时重传是用来兜底的。

数据丢失它可能丢失一个或多个,但滑动窗口的左侧指针指向的这个报恩一定是他丢失的一个或多个报恩当中序号。最小的一个报恩。
所以呢这个最小的报文呢,那么它后续就有可能有很多报文,就意味着后续有很多应答,所以这样的报文,我们就能够触发它尽快的进行重传而一旦通信接近未期的话呢,那么,我们此时
也可以采用我们的超时重传来对我们的丢包做补发。

3.2移动原则

滑动窗口只能往右移动不能往左移动,并且这个窗口大小既可以变大,又可以变小,甚至可以为0。是动态变化的。

知识点1:移动的算法:
start=接收到的确认序号
end=接收到的确认序号+对方的窗口大小。
并且滑动窗口的移动不会越界,因为发送缓冲区是采用环循队列的形式构成的。

4.确认应答机制(避免丢包)

在这里插入图片描述

TCP凭什么保证可幕性,最基本的一个特点:确认应答机制

客户端和服务器双方地位是对等的,那么如果客户端给服务器发了个
消息,即便是服务器没有任何消息想给我们对应的客户端说,服务器至少也要对这个消息进行一次响应叫做确认应答。
如果服务器给我们对应的客户端发消息,发完数据之后呢,客户端也需要进行确认应答。

确认应答机制,它的本质其实是使用了我们对应的一个
叫做局部可靠性,
只要我们收到了应答,我就能保证历史消息被对方收到
TCP保证可靠性主要依靠应答机制。它的最核心的点就在于只要我收到了应答,我能保证我刚发的消息,对方一定收到了。
同样的道理,对方给我发消息,只要对方收到了我的应答,就能保证我发的消息,对方也能收到了。通信就是这样的,就是我要可靠
的把我的消息发给你,你要可靠的把消息发给我,这样的目的达到了
达到了,只要我收到确认应答就行了。
如果没有收到确认应答,就说明数据丢了。所以根据确认应答机制可以保证一端的可靠性,保证不丢包。一旦丢包了就能知道。
另一端也采用该机制,就可以该端的可靠性。从而实现两端的可靠性。

4.1捎带应答(提高效率)

客户端给服务器发消息,发了消息之后呢,那么服务器可能也想给我
发消息,他此时直接给我做应答的同时把他的数据也带
上,他的数据就是有效载荷,那么此时把它捎带带上,那么我
们就通过发一个包的同时既可以对你上一个报文做应答,又可以把我
服务器要发送的消息,也给你发过来,这就叫做捎带应答,客户端也可以采用同样的策略。

只要我们双方互有应答,只要我收到了应答,我就能保证我这个朝向上的可靠性
虽然我们不保证应答的可靠性,但我能保证,旧数据的可靠性,就好比我们不保证最新一条消息的可信,但我们保证历史消息的可靠性一样。

4.2延迟应答(提高效率)

我们要知道,发送方一次性发送更多的数据,就表明发送的效率就越高。网络的吞吐量就越大。
可是发送方发送的数据受接收方缓冲区剩余空间大小的限制,那么如何让接收方发送方通告一个更大窗口大小呢?
方法:延迟应答,即让接收方收到报文不着急发送应答!留给上层较为充分的时间,将数据取走。再发送应答时,接收窗口就会变大。

知识点1:延迟应答
如果接送方收到数据后立刻返回ACK应答,这时候返回的接收窗口比较小。所以我们要延迟一会,等待上层读取一些数据,让接收窗口变大些,再发送ACK应答。
一定要记得,窗口越大,网络的吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率!

5.序号与确认序号(避免乱序)

在这里插入图片描述
如果客户端在短时间内发送了许多报文,这些报文会因为网络,中间设备等原因,不一定能按序到达,比如报文1先发送,但因为网络堵塞,最后一个才到,这种情况在服务器接受时,就存在报文乱序的情况。而TCP是可靠的,它需要保证报文的按需到达。
它是如何实现的呢?TCP是根据序号来保证报文的按序到达的,每个报文都有自己唯一的序号。这样对端只要根据序号就可以知道哪个报文先发送过来。

tcp是面向字节流的,tcp把上层接收的数据都看成一个个大的字符
串,存放在tcp的缓冲区里,发送缓冲区就可以看成对应的字符数组。
所以每个字节就有自己的编号(本质就是数组的下标)。
在这里插入图片描述


序列号
1.每个报文都要携带序号,这个序号的设定呢,我们认为它就相当于报文在字符数组(接受缓冲区)的最后一个元素的下标,这个就是它的序号。在这里插入图片描述

客户端一次给对方发送了多个报文,那么对方也一定会有多个响应给我,那我怎么知道对应的响应是谁呢?比如说这个响应是对应哪个报文的响应呢?

tcp协议为了能区分应答分别是对哪一个报文的响应,它就有
了一个叫做确认序号的概念
。这个确认序号是在报头字段里

确认序号
确认序号=收到报文的序号+1,只要根据应答报头里面的确认序号,将其减一,得到的就是发送报文的序号。发送方就知道该应答是哪个报文的应答了。
确认序号的意义:表示确认序号之前的数据已经全部收到了,下一次发送,请从确认序号指定的数字开始发送!。


[问题]:为什么要存在序号和确认序号,为什么不直接复用序号?
发送端的报文里,携带了序号,接收端接收后就能知道该序号,直接复用该序号,发送回去不就好了吗?为啥还需要一个确认序号呢?

存在一种情况:
客户端给服务器发消息,服务器要给我应答,服务器给我应答,应答只是纯报头,可是它也可能也想给我发消息,所以它也可能携带数据,所以对于服务器来讲,它发送的报文叫做捎带应答,它既是应答又是我们发过来的数据,所以对于服务器给客户端响应应答,那么此时就使用确认序号,而此时它本身也携带数据,客户端也需要知道发送报文的顺序,所以它就使用序号。
所以在这种情况下,必须使用序号和确认序号。

一个报文的,它本身就可能会有两重身份,既可能是应答,又可能是携带了数据的应答,因为有捎带应答的,所以呢我们的序
号和确实序号必须同时存在。

应答==>使用确认序号
应答+数据==>使用确认序号和序号

6.超时重传(避免丢包)

当一个数据被发出去的时候,发送方没有收到应答,其实我们也并不清楚这个报文到底是不是真的丢了,

因为两端主机可能相隔千里之外,数据传送的时候,它的时
间出现震荡是很正常的。

什么意思呢,就是客户端把一个报文发出去,这个报文究竟怎么回事客户端一概不知,只有一种情况能确定报文情况:我收到了应答,才能确定报文被对方收到了。其他情况客户端都是不清楚的。

判定一个报文是否是丢失呢,它是被规定出来的:
因为报文丢失只有两种情况,要么数据丢了,要么应答丢了。而这两种情况,客户端或者服务端都不清楚,只能硬性规定:
在特定的一种时间间隔中,如果发现没有应答,就认为我刚刚发过去的消息,没有被对方接收到,这个报文极有可能丢失了。

【情况一】数据丢了
在这里插入图片描述

【情况二】应答丢了
这个数据它没丢,反而是应答丢了,应答丢了,此时,可能会导致主机没有收到对应的应答,所以主机没办法判定这个数据到底是不是真的丢了,所以规定只要我超时范围内没有收到应答,我立马对它做补发。但是会存在一个问题:对端是接收到报文了,只是应答丢了,补发,那么报文就会出现重复,这时候,就需要根据报文的序列号进行去重。在这里插入图片描述

那么超时的时间是如何确定的呢?

  • 最理想的情况下, 找到一个最小的时间, 保证 “确认应答一定能在这个时间内返回”.但是这个时间的长短, 随着网络环境的不同, 是有差异的.
  • 如果超时时间设的太长, 会影响整体的重传效率;
  • 如果超时时间设的太短, 有可能会频繁发送重复的包;

TCP为了保证无论在任何环境下都能比较高性能的通信, 因此会动态计算这个最大超时时间

  • Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时 时间都是500ms的整数倍.
  • 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
  • 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.
  • 累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接.

7.拥塞控制(网络可靠性保证)

要知道以上所有所有的策略,都是在两端的机器上起作用的,因为两端机器都安装了TCP协议。对于接收到的数据内核自动处理。
但是要明白网络呢,是不属于客户端和服务器的,所有客户端和服务器对于网络出现的问题是无能为力的,比如网络硬件出现问题,数据量太大了,引起阻塞。不过也不用担心,因为TCP不仅考虑的双方两个主机各自的策略还考虑的了网络的问题。

知识点1:拥塞控制
①如果通信时出现少量的丢包,tcp认为是常规情况
②如果通信时出现大量的丢包(大量的数据都超时了),tcp会认为网络出现问题了。

这时候发送方应该怎么办呢???
发送方这时不能立即对报文进行超时重发,少发送一点数据,因为网络已经堵塞了,再发送只会加剧堵塞。

知识点2:拥塞共识
当网络出现问题时,因为每台主机都是用的TCP协议,所有它们都能意识到网络堵塞了,就要少发送一点数据。并不是针对某一个主机少发送数据,而实让那些遇到网络堵塞的主机少发送一点数据。
在这里插入图片描述


知识点3:方案(慢启动)
要求:所有识别到网络堵塞的机器都要执行该策略
TCP在识别到网络发生拥塞时,它就会执行慢启动机制!先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据;


知识点4:拥塞窗口
①拥塞窗口就是网络的接收能力,慢启动刚开始时,拥塞窗口为1,每发送一个报文出去并接收到ACK时,拥塞窗口大小就会乘2。
②每次发送数据时,发送方的滑动窗口实际大小其实是,对方的接收窗口和拥塞窗口较小值。也就是发送方的发送能力是受对方的接收能力和网络的接收能力决定的。
③所以TCP的滑动窗口(发送能力)既考虑了对方的接收能力,又考虑的网络的接收能力。

8.TCP总结

在这里插入图片描述


通信双方在传输数据时的传输细节问题,应用层不关心,比如丢包重传,应答机制,都不关心,
在这里插入图片描述
应用层的工作只是,对结构化数据进行序列化,报文封装,通过网络传输,报文解包,然后进行反序列法转化成结构化数据进行业务处理,处理完之后构建response序列化形成字符串,然后拼接成报文发送出去,这些工作是由应用层来做的。

而我们tcp往下的所有协议是共同来处理通信细节的,它的通信细节体现在对应的丢包,重传,确认应答,三次握手四次挥手等。

四.TCP三次握手四次挥手

在这里插入图片描述

1.理解三次握手四次挥手

知识点:三次握手都干了什么?
①建立连接
②协商起始序号(一开始是随机的)
③协商对方接收缓冲区大小

在这里插入图片描述
连接为什么是三次呢,其实主要核心点是因为中间的这两个报文,它被我们进行捎带应答了。

正常的通信应该是四次,只不过可以被压缩成三次,它的本质其实是 一来一回的一种可靠性,相当于我给你发消息,你给我应答,你给我发消息,我给你应答,所以我们双方一定可靠的互相至少给对方发送了一次消息,对于客户端和服务端其实都是如此。
如果少了任何一个报文,tcp的可靠性就无法保证了,所以TCP连接其实本质是四次,只不过被捎带应答了,所以就变成了一个三次,一个四次。

那为什么能捎带呢,为什么挥手不能捎带呢?
因为连接是一方主动,一份被动,客户端主动发起连接,服务器端被动接收连接,也就是服务器端必须要接收连接。所以客户端发起SYN请求,服务器端也必须发起SYN请,服务器端还需要应答客户端,所以肯定可以捎带。

而断开连接需要双方的同意才能断开,客户端发起断开,没有什么话跟服务器说了,但服务端可能还有消息没给你发完呀,客户端可以先给我断开链接,但是服务端其实也可以继续给你发消息的啊。
所以有可能在我们一方想断开链接时,另一方并不想断开链接,所以你想让这两个压缩成一个得有巧合性存在,所以大部分情况下它俩是分开的,所以是4次。

2.两个关键函数

客户端的connect()函数和服务器端的accept()函数;

connect()函数
第一,connect函数,它只负责发起三次握手,至于三次握手的细节,实际上它并不过多的参与这个握手的过程,是双方操
作系统自主完成的,connect在发出SYN报文之后呢,你可以理解成
它此时就处于阻塞状态,等待三次握手完成,完成之后establish了,connect才会返回。


accept()函数
第二,accept函数,它负责获取链接,它本身并不参与三次握手,它只会把已经建立好的链接直接拿上来,这叫做accept,如果底层没
有建立好的链接,那么accept就会一直阻塞住。服务器端的链接建立跟上层有没有accept没有关心。在建立连接之前accept就已经在阻塞等待。

3.建立连接时间不同

客户端是在发出ACK报文之后就表示链接建立成功,而服务器端是在接收到客户端发出的ACK报文后,链接才建立成功。

4.为什么要三次握手四次握手

理由一:三次握手可以验证全双工
为什么要立链接?建立链接的本质就是在测试当前我们的网络状态适合通信,所以我们双方在进行三次握手的时候,我们先要保证双方都能互相收发,要不然你都保证不了,你就没办法基于tcp的全双工来进行通信。
并且三次握手是验证全双工的最小次数


理由二:三次握手可以降低服务器端的成本
奇数次的握手可以将连接失败的成功嫁接到客户端,而不是让服务器端承担。

断开链接需要双方都同意,协商后才能断开,所以客户端发起一次断开连接请求,服务器端也发送一次断开连接请求,中间不能捎带,所以是四次。

5.三次握手的状态变化

在这里插入图片描述

知识点1:服务器端连接建立成功和上层有没有accept函数没有关系,就算上层没有accept函数,连接也可以建立成功,accept在三次握手之前就在阻塞中。
establish表示连接建立成功

一旦连接建立成功,操作系统就需要对那些没有被上层accept获取的链接进行管理,系统里采用队列的形式来管理已经建立好的链接。

链接进入队列中后,就可以供上层调用accept来获取链接。所以三次握手,每一次建立一个链接,本质就是将该链接链入队列中。而accept的本质就是从该队列中把链接获取走,和特定的文件描述符管理起来给我们返回特定的套接字。
这个队列就叫做全连接队列
在这里插入图片描述


知识点2:这个队列的长度就表示服务器端能够获取连接的最大长度,也就是暂时不被accept获取上去的链接最大个数。
listen的第二个参数+1就表示该全连接队列的最大长度是多少


比如listen()函数的第二个参数是1,则表示全连接队列最多能容纳两个链接。
那客户端如果同时发起三个连接请求,会怎么样呢?

服务器端前两个三次握手,连接会建立成功,放入全连接队列里,
而第三个连接则会失败,系统会硬性要求服务器将最后一个ACK
直接丢弃,进而无法完成三次握手,也就无法将进入establish状态。

注意:客户端的连接是建立成功的,并且ACK也发送出去了,这就会
导致client端和server端连接建立不一致问题

知识点3:当超过listen第二个参数大小的数量的连接会直接将ACK丢弃,进而转换为SYN_RECV状态,也称为半连接,所有的连接肯定是先经过半连接再变成全连接。
并且server端,不会长时间维护syn_recv,一段时间后就会自动释放,被建立连接的一份,处于syn_recv,半连接,半连接队列,半连接的结点,不会长时间维护


知识点4:那listen的第二个参数,为什么不能太长,为什么不能没有呢?
①不能太长:如果上层很忙,来不及获取连接,那么多的连接就光占用资源,不创造价值呀。
②不能没有:如果上层突然空闲了,就无法直接获取连接了,不能快速的将建立好的连接补上来,服务器的资源就不能得到充分的利用

6.四次挥手的状态变化


主动断开连接的一方,会四次挥手之后,会进入time_wait状态,等待若干时间,自动释放。

也就是并没有完全将连接释放掉,还处于一种time_wait的状态。
比如服务器端先发起断开请求,那么客户端同意后,服务器端的连接就变成time_wait状态。

知识点1:地址复用getsockopt()
当请求连接的个数超过了服务器的极限,服务器挂了,这种服务器就属于主动断开连接的一方了,服务器就会处于time_wait的状态,这时候就无法立即重启服务器,因为原来的请求并没有真正的释放。
在这里插入图片描述
一般服务器挂了要尽快重启,减少损失,所以为了应对这种情况,系统是允许地址复用的。可以通过getsockopt设置套接字的状态,使服务器尽管还在time_wait状态,仍可以重复使用端口。


知识点2:TIME_WAIT要等待多长时间呢?
它要等待2MSL,约60s-120s
报文在网络中存活的最长时间叫做MSL,2*MSL就能保证报文至少能在通道里来回一次。


知识点3:为什么要进行等待呢?
理由1:让通信双方未完成的历史数据得以消散。2MSL能保证客户端和服务器历史上发送的一些还并未收到的报文,在这个等待的时间被彼此收到,这个收到其实本质是丢弃掉报文,避免历史数据对后续通信造成干扰
理由2:提高四次挥手完成,断开连接的容错性。可能最后两次握手时,ACK丢失了,那么在time_wait等待期间,还有机会再重新接收到你给我补发的FIN报文,再重新发送ACK应答。这就可以保证最后两次挥手不会失败。

相关文章:

  • 嵌入式硬件篇---JSON通信以及解析
  • 给Android Studio配置本地gradle和maven镜像地址,加快访问速度
  • Vue3 视频播放与截图功能实现
  • 第六章、Isaacsim中的资产:usda文件详解(1)
  • 基姆拉尔森计算公式
  • 车辆投保日期查询API:快速获取想要的车辆保险日期
  • [王阳明代数讲义]琴语言类型系统工程特性
  • Tracing the thoughts of a large language model 简单理解
  • AI比人脑更强,因为被植入思维模型【41】反作用力思维模型
  • Python 爬虫突破反爬虫机制实战
  • 文献分享: DESSERT基于LSH的多向量检索(Part1——原理与实现)
  • C++中std::priority_queue的使用说明
  • #MySQL 语句大全(完整实用教程)
  • 生成对抗网络(GAN)详解
  • Java代理(四)动态代理之CGLIB
  • 单网卡上绑定多个虚拟IP(AI回答)
  • linux部署成功,但外网无法访问
  • 数据结构与算法:子数组最大累加和问题及扩展
  • 百度查询的ip与命令行输入 ipconfig 显示的IP地址有以下主要区别:
  • 管家婆财贸ERP BB102.采购销售订金管理