目次
1.TCP协议
1.1.什么是TCP协议
1.2.为什么TCP叫传输控制协议
1.2.TCP是面向字节省的
2.TCP协议段格式
2.1.流量控制——窗口大小(16位)
2.2.确认应答机制
2.2.1.什么是确认应答机制
5.2.2.推导确认应答机制
5.3.2.确认号和序列号
2.3.六位标志位
3.TCP的可靠策略
3.1.确认应答机制
3.2.超时重传机制
3.3.连接管理机制
3.3.1.怎样理解TCP连接
3.3.2.连接——三次握手
编辑
3.3.3.断开连接——四次挥手
1.TCP协议
1.1.什么是TCP协议
- TCP 是面向连接的运输层协议。应用步伐在使用 TCP 协议之前,必须先创建 TCP 连接。在传送数据完毕后,必须释放已经创建的 TCP 连接
- 每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一)
- TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,而且按序到达
- TCP 提供全双工通讯。TCP 答应通讯双方的应用历程在任何时间都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通讯的数据
- 面向字节省。TCP 中的“流”指的是流入到历程或从历程流出的字节序列
1.2.为什么TCP叫传输控制协议
我们之前创建的TCP套接字,实际上sockfd会指向一个操纵体系给分配好的socket file control block(socket文件控制块),而这个socket文件控制块内部会维护网络发送和网络接收的缓冲区,我们调用的所有网络发送函数,write send sendto等实际就是将数据从应用层缓冲区拷贝到TCP协议层,也就是操纵体系内部的发送缓冲区,而网络接收函数,read recv recvfrom等实际就是将数据从TCP协议层的接收缓冲区拷贝到用户层的缓冲区中,而实际双方主机的TCP协议层之间的数据发送是完全由TCP自主决定的,什么时间发?发多少?发送时出错了怎么办?
这些全部都是由TCP协议自己决定的,这是操纵体系内部的事情,和我们用户层没有任何瓜葛,这也就是为什么TCP叫做传输控制协议的原因,因为传输的过程是由他自己所控制决定的。
c->s和s->c之间发送使用的是不同对的发送和接收缓冲区,以是c给s发是不影响s给c发送的,这也就能分析TCP是全双工的,一个在发送时,不影响另一个也再发送,以是网络发送的本质就是数据拷贝。
说应用层缓冲区怕大家感觉到抽象,实在所谓的应用层缓冲区就是我们自己界说的buffer,可以看到下面的6个网络发送接收接口都有对应的buf形参,我们在使用的时间肯定要传参数进去,而传的参数就是我们在应用层所界说出来的缓冲区。
这里多说一句,上面的六个接口在进行网络发送和网络读取数据的时间,都会做网络字节序和主机字节序之间的转换,recvfrom和sendto是步伐员自己表现做转换,其余的四个接口是操纵体系主动做转换,这是铁铁的事实!
1.2.TCP是面向字节省的
当用户消息通过 TCP 协议传输时,消息可能会被操纵体系分构成多个的 TCP 报文,也就是一个完备的用户消息被拆分成多个 TCP 报文进行传输。
这时,接收方的步伐如果不知道发送方发送的消息的长度,也就是不知道消息的界限时,是无法读出一个有效的用户消息的,因为用户消息被拆分成多个 TCP 报文后,并不能像 UDP 那样,一个 UDP 报文就能代表一个完备的用户消息。
举个实际的例子来分析。
发送方预备发送 「Hi.」和「I am Xiaolin」这两个消息。
在发送端,当我们调用 send 函数完成数据“发送”以后,数据并没有被真正从网络上发送出去,只是从应用步伐拷贝到了操纵体系内核协议栈中。
至于什么时间真正被发送,取决于发送窗口、拥塞窗口以及当前发送缓冲区的大小等条件。也就是说,我们不能认为每次 send 调用发送的数据,都会作为一个整体完备地消息被发送出去。
如果我们思量实际网络传输过程中的各种影响,假设发送端陆续调用 send 函数先后发送 「Hi.」和「I am Xiaolin」 报文,那么实际的发送很有可能是这几种情况。
第一种情况,这两个消息被分到同一个 TCP 报文,像这样:
第二种情况,「I am Xiaolin」的部分随 「Hi」 在一个 TCP 报文中发送出去,像这样:
第三种情况,「Hi.」 的一部分随 TCP 报文被发送出去,另一部分和 「I am Xiaolin」 一起随另一个 TCP 报文发送出去,像这样。
类似的情况还能举例许多种,这里主要是想分析,我们不知道 「Hi.」和 「I am Xiaolin」 这两个用户消息是怎样进行 TCP 分组传输的。
因此,我们不能认为一个用户消息对应一个 TCP 报文,正因为这样,以是 TCP 是面向字节省的协议。
当两个消息的某个部分内容被分到同一个 TCP 报文时,就是我们常说的 TCP 粘包问题,这时接收方不知道消息的界限的话,是无法读出有效的消息。
要办理这个问题,要交给上层的应用层。
2.TCP协议段格式
图中,每一行有 4 个字节(32位),解包步骤如下:
提取报头:
- 除了选项之外的报头叫做标准报头,一共 20 字节。
- 提取选项:根据 4 位首部长度获取报头的整体大小,减去 20 字节的标准报头(固定报头),得到选项。如果没有选项的话就能直接得到有效载荷。
提取有效载荷:有效载荷 = 报文-报头 (-选项)
- 16位源端口:发送方主机的应用步伐的端标语
- 16位目标端口:目标主机的应用步伐的端标语
- 32位TCP序列号:表现本报文段所发送数据的第一个字节的编号
- 32位TCP确认序号:接收方盼望收到发送方下一个报文段的第一个字节数据的编号
- 4位TCP首部长度:数据偏移是指数据段中的“数据”部分起始处间隔TCP报文段起始处的字节偏移量。确定TCP报文的报头部分长度,告诉接收端应用步伐,数据(有效载荷)从何处开始
- 6位保留字段:为TCP将来的发展预留空间,目前必须全部为0
- 6位标志位:共有6个标志位,每个标志位占1个bit
- 16位窗口大小:表现发送该TCP报文的接受窗口还可以接受多少字节的数据量。该字段用于TCP的流量控制
- 16位校验和字段:用于确认传输的数据有无损坏 。发送端基于数据内容校验天生一个数值,接收端根据接受的数据校验天生一个值。两个值相同代表数据有效,反之无效,扬弃该数据包。校验和根据 伪报头 + TCP头 + TCP数据 三部分进行计算
- 16位告急指针字段: 仅当标志位字段的URG标志位为1时才有意义。指出有效载荷中为告急数据的字节数。当所有告急数据处置惩罚完后,TCP就会告诉应用步伐规复到正常操纵。纵然接收方窗口大小为0,也可以发送告急数据,因为告急数据无须缓存
- 选项字段:长度不定,但长度必须是32bits的整数倍。内容可变,因此必须使用首部长度来区分选项的详细长度
TCP 的报头是变长的,包罗固定的 20 字节和变长的选项。其中,“数据偏移”也叫做“首部长度”,它占固定 4 位,作用是保存报头整体的长度,以便接收端可以大概精确解析报文中的字段。
值得注意的是,固然首部长度占 4 个比特位,4 个比特位能表现的范围 0~15。但是它的单元并不是1字节,而是4字节,以是它实际能表现的范围就是[0,60]字节。
而我们看到,选项上面的报头是固定字节大小,也就是20字节,那么分析选项最大可以有40字节。但是我们本日不谈选项,因为固定首部20字节,以是选项字段最长40字节。当没有选项字段时,首部长度字段为5(20 = 5*4),即0101。
每层协议的学习,我们都应该掌握这两个问题:
- 协议报头和有效载荷怎样分离?
- 有效载荷怎样向上交付?
我们看到,TCP首部包罗20字节的固定长度首部和选项字段,同时固定首部中有一个首部长度字段(4bits)。
值得注意的是,固然首部长度占 4 位,但是它的单元是 4 个字节,那么 4 个比特位能表现的范围 0~15,就能表现 0~60 字节。
首部长度最大可表现15(因为4位比特位最大是1111,即是15),但是它的单元是 4 个字节,即TCP首部最长为15 x 4 =60字节。因为固定首部20字节,以是选项字段最长40字节。当没有选项字段时,首部长度字段为5(20 = 5*4),即0101。
提取报头:
- 除了选项之外的报头叫做标准报头,一共 20 字节。
- 提取选项:根据 4 位首部长度获取报头的整体大小,减去 20 字节的标准报头(固定报头),得到选项。如果没有选项的话就能直接得到有效载荷。
提取有效载荷:有效载荷 = 报文-报头 (-选项)
这样子就 通过首部长度,我们就可以将TCP首部和有效载荷分离。
TCP是传输层的,上层是应用层。而应用层步伐会绑定端标语,TCP首部中有16位目标端标语,根据端标语做到向上交付。
接下来我们要进入TCP协议的深入学习,下面会出现比方这样子的图
这个SYN和ACK是哪里的东西啊?
我们肯定要注意:
客户端和服务端基于TCP协议进行通讯,每次互发消息的时间,发送的但是完备的tcp报文,即肯定携带完备的TCP报头。
2.1.流量控制——窗口大小(16位)
说到可靠传输,那么不可靠传输是什么样的呢?
好比数据传输重复,出现乱序,出现丢包等等情况,都是不可靠。因此可靠的数据传输,肯定要规避这些情况,
在TCP协议段格式中,在标准报头中,我们看到有个16位窗口大小,这是什么呢?
我们先增补一点知识来,
我们知道,TCP协议有发送缓冲区和接收缓冲区,无论是用户端照旧服务端都是。
假设有一天,服务端太忙了,客户端在向服务端发送数据,但是服务端来不及调用read或者recv这样的接口来拿取数据,但是客户端并不清除服务端那里的情况,就不停向服务端发,服务端已经被写满了,依旧再发的话,那么就会导致出现大面积丢包现象。
因此,为了规避这种情况,当服务端的接收缓冲区空间告急的时间,我们应该想办法让用户端发送数据的速度慢点,或者直接不发了。
以是,这种通过控制客户端发送数据的速度,以便能让服务端来得及处置惩罚数据,从而规避大面积丢包的情况,这种策略就叫做流量控制。
别的说下,TCP协议实在另有一个策略叫做数据重传,也就是如果数据丢包了,那么就重新传一份。
因为我们也知道,一个TCP协议的报文,光是标准表头就是固定20字节大小,还不算选项和有效载荷,如果出现大面积丢包,重传确实可以办理问题,但是浪费的资源太多,服从太低效。
大家还需要理解一下:双方通讯发送的每一条消息里面都会有TCP协议报头
答案就是服务端在返回给客户端的相应中,16位窗口大小就是服务端的接收缓冲区当前还剩多少空间。客户端就可以根据这个剩余的空间来订定公道的发送数据的速度。
而且,我们要能想到,服务端也可能会给客户端发消息,那么客户端也会给服务端相应,那么此时这个相应中的16位窗口大小就是客户端的接收缓冲区还剩多少空间。
说白了这个16位窗口大小就是对方的接收缓冲区还剩多少空间
也就是说,双方都可以进行流量控制。
2.2.确认应答机制
2.2.1.什么是确认应答机制
TCP包管数据安全传输的时间最基本的一个特点就是确认应答机制。
本质原因照旧因为传输间隔过长。
好比我在内蒙给广东的网友发送消息,那数据包实在是要颠末许多的路由器结点进行数据包转发,穿过许多的局域网,在局域网内部颠末双绞线(以太网技能常用的物理介质)传输,还要颠末运营商的基站,数据包在云云之长的传输间隔中很有可能会丢失,数据里面的比特位翻转,又或是数据包中的字节乱序,又或是数据包重复发送给我的广东网友(发送方可能以为数据包丢失了)。
需要确认应答(acknowledgement)机制
客户端在向客户端发送数据的时间,每次发送完一个数据,客户端不会马上接着发数据,而是会等候从服务端传来的应答后,再发送数据,这样就能制止数据传输中不可靠的问题
固然数据包在网络中传输的间隔过长,但只要我发送给我网友的消息有复兴,有应答,那我就能判断我发的数据肯定到达了我网友的主机上。
好比我问我网友,你TCP/IP学的怎么样啊近来?我网友给我复兴说,我近来正学TCP的确认应答机制呢!那我立马就可以肯定我发送的数据颠末网络传输后,我的网友肯定收到了,因为网友对我发送的消息做出了复兴。同样的,如果我没复兴我的网友,那网友也不敢确定他说的话,我肯定收到了,因为我还没有给他发送的消息做出复兴呢!而这就是典型的确认应答机制!
但实在你可以发现,我和我的网友在发消息时,总会有末了一条消息是没有被确认的,无论末了一条消息是他发照旧我发,以是我们可以得出结论,TCP并没有绝对的可靠性,只有相对的可靠性!事实上,不只是TCP,所有的协议都是没有绝对的可靠性!
以是TCP的可靠性永远不谈最新的消息,只评论汗青的消息,因为肯定存在最新的一条消息是没有被应答的。
但是话又说回来,我们实在只要包管客户端到服务端的数据完好就可以了,服务端没须要非要知道自己的相应是否完好送达,因此客户端发送数据后,只要一段时间内没收到来自服务端的相应,那么无论什么原因,都认为这次发送失败了,就会进行重发。
总之,最新的一条消息(也就是应答),是没有应答的(看起来有点绕,也就是应答不需要应答)。
5.2.2.推导确认应答机制
我们直接来看看我们预想的是怎么做的?
这样子可靠性就包管了 ,但是这样子服从太低了。
难道我说一句话,你都要先回一句:“我听到了”,然后才说你想回应的话吗?这这个服从太低下了,精确的做法应该是将应答和想复兴的消息一起发送回去
应该是下面这个
我们不包管最新一条信息的可靠性,我们只包管汗青消息的可靠性!
但是现在又有问题了,难道我发信息之前都要等你的应答吗?没有你的应答我是不发了吗?
不可能。真实的TCP是发送消息时,会一次性发送一批数据段,确认应答时,也会一次性发送一批确认数据段。
发送方可以同时发送多条数据,接收方根据收到的消息给出复兴,但存在“后发先至”问题,接收方收到的数据顺序被打乱,这可能引起严重的歧义。
比方下面这种情况,双方最终会造成误解。
针对“后发先至”问题,TCP的办理办法是给传输的数据和应答报文都进行编号。
5.3.2.确认号和序列号
客户端在接收到相应之前,照旧会把数据存在缓冲区里。
首先,我们客户端要发送的数据,已经存在TCP的发送缓冲区(内核里面的那个)中了,因为TCP是面向字节省的,这个缓冲区我们可以看作是char类型的大数组,那么每一个空间就是一个字节,而且另有对应的下标,那么也就是说,每一个字节天然就有自己的编号。
我们拷贝在缓冲区里的数据是按顺序存储的。
我们只需要在缓冲区里的数据是按顺序存储的即可!!!
序列号
- 含义:序列号是指一个TCP报文段中第一个字节的数据序列标识。它表现在一个TCP连接中,该报文段所携带的数据的开始位置。序号是用来包管数据传输的顺序性和完备性的。
- 作用:在TCP连接创建时,双方各自随机选择一个初始序列号(ISN)。随后传输的每个报文段的序号将基于这个初始值递增,其增量为该报文段所携带的数据量(字节数)。通过这种方式,接收方可以根据序号重组乱序到达的数据片断,确保数据的精确顺序和完备性。如果接收到的报文段不连续,接收方可以通过TCP的重传机制哀求发送方重新发送缺失的数据。
比方,如果一个报文段被赋予了序号100,而且它包罗100字节的数据,那么这个报文段就代表了从序号100到199的数据。随后的报文段将继续这个序列。继续上面的例子,下一个报文段可能会开始于序号200,如果它包罗50字节的数据,那么它就代表了从序号200到249的数据。
我们可以简单的理解为数据里面每个字节都有唯一的标识——序列号。
增补知识
- 在TCP中,当发送端的数据达到接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做ACK(确认应答,PositiveAcknowlegement)
- 在TCP中,当发送端的数据达到接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做ACK(确认应答,PositiveAcknowlegement)
- 每一个 ACK(Acknowledge应答) 都带有对应的确认序列号,意思是告诉发送者,我已经收到了确认号之前的所有数据,下一次你从确认号开始发.
- 服务器和客户端之间需要有办法能区分出, 当前这个报文是普通报文, 照旧确认应答报文. 标志位中的 ACK 就可以办理, ACK 为 0 时, 表现这是一个普通的报文, 此时只有 32 位序号是有效的, 当 ACK 为 1 时, 表现这是一个应答报文, 这个报文的序号和确认序号都是有效的.
确认号
确认序号的界说却是这样的,确认序号的值表现接收方收到了确认序号之前的所有报文,而且是连续的报文,好比确认序号的值是11,那就代表接收方收到了10号及之前所有序号的报文,发送端下次从第11序号开始发送报文就可以了,以是确认序号的值从发送方的角度来理解,可以理解为发送方下一次发送报文时,报文的序号的值。
- 含义:确认应答号是接收方盼望从发送方接收到的下一个报文段的序号。它实质上是接收方告诉发送方:“我已经成功接收到了哪个序号之前的所有数据,请从这个序号开始发送后续的数据。”
- 作用:确认应答号用于实现可靠性传输。当一个报文段被接收方精确接收时,接收方会发送一个ACK报文,其中包罗的确认应答号是接收到的数据加上1(即接收方盼望接收的下一个数据的序号)。通过查抄这个确认应答号,发送方可以大概知道其发送的数据是否已被接收方精确接收,并据此决定是否需要重传某些数据段。
但是这里我们可以思索一个问题,
- 在这个场景下,我们实在只需要一个32位序号就可以办理问题了,没须要再使用一个确认序号,那TCP为什么要筹划两个序号呢?
可以从两个场景来理解:
- 1.偶然间,一个报文可能有双重身份,好比服务器除了返回应答,它还想给客户端传送数据,这种既是应答又是数据的相应,叫做捎带应答,那么此时确认序号就是为了告诉客户确认序号的前面的数据都已经收到了,序列号就是告诉客户端,从服务器发来的数据的第一个字节的序列号是多少的。
- 2.偶然间,并不是总是客户端给服务器发消息,服务器也会给客户端发消息,双方地位是对等的,如果只用一个序号位,就搞不清谁发谁收,因此照旧需要两种序号位。
别的我们要知道,服务端一般都是一批数据连着发,如果是发一个数据,还得等服务器返回一个应答之后再继续发,那样服从太低了。
但是这里有一个问题,就是这批数据原来是按肯定顺序发送的,但是服务器却不肯定按相应顺序接收的。这就是数据乱序问题。UDP是没有办法的,毕竟是不可靠传输,那么TCP是怎样办理的呢?
这里照旧得靠TCP报头中的32位序号,它除了可以大概确认应答,还能包管数据按序到达!
序列号和确认序号的作用:
- 将哀求和应答逐一对应起来;
- 确认序号表现的是它之前的数据已经全部收到;
- 答应部分确认应答丢失,或者不发送确认应答;
- 包管了 TCP 的全双工通讯。
2.3.六位标志位
标志位在哪里啊?
标志位字段共6bit,有6个标志位,每个标志位1bit,即只有0和1两种状态。
有以下这6位标志位
TCP标志位
| 中文意思
| 作用
| SYN
| 同步标志
| 用于创建连接
| ACK
| 确认标志
| 用于确认收到数据
| FIN
| 结束标志
| 用于关闭连接
| RST
| 重连标志
| 用于重置连接
| PSH
| 敦促标志
| 用于立刻传输数据
| URG
| 告急标志
| 用于指示告急数据
| 接下来来详细先容一下
1.ACK 表现本报文前面的确认号字段是否有效:只有当ACK=1时,前面的确认号字段才有效;
在TCP确认复兴机制中,客户端和服务端任意一方发送数据后,另一方都需要给予应答以表明自己收到数据。在应答的报文中该标志位需要置1,同时应答的报文也可以携带数据。
TCP规定,创建连接后,ACK必须为1
2. SYN 哀求创建连接。携带SYN标识的称为同步报文段。
作为服务端,我怎么知道客户端是想发信息照旧想和我创建连接呢?那就需要这个标志位了。
SYN表现同步标志位,实在就是申请创建连接的标志位。
TCP是面向连接的协议,双方在正常通讯之前需要先创建连接,创建连接是一个三次握手的过程。
- 第一次,Client 给Server发送哀求连接,报文中携带SYN标志位来表明当前报文是和创建连接相干的。
- 第二次,Server接受Client的连接,并扣问何时创建连接,报文中也会携带SYN标志位,同时会携带ACK往返复上一条哀求。
- 第三次,Client 复兴 Server,立马创建连接,这里只是单纯的复兴,只要设置ACK即可。
那么问题来了,站在Client的角度,什么时间认为连接已经创建起来了呢?答案是第二次握手以后,即收到Server的复兴。
然而站在Server的角度,创建连接的时间是在第三次握手,因为第二次握手只是Server端片面同意了,Client端有没有同意是在第三次握手才知道。
注意:TCP固然包管可靠性,但是TCP答应连接创建失败。
- client向server哀求创建连接:当SYN=1,ACK=0时,表现该报文为哀求创建连接的报文;
- server向client哀求创建连接:当SYN=1,ACK=1时,表现同意创建连接;
只有在创建连接的前两次哀求中SYN才为1。该报文称为同步报文段
3.FIN FIN表现结束标志位,可以理解为Finish,实在就是断开连接的标志位。
断开连接是一个四次挥手的过程。
Client 片面断开连接需要两次;Server端片面断开连接需要两次,加起来就是四次。
谁先断开连接,这个没有限定,下面就假设Client先断开。
- 第一次挥手,Client 通知 Server 自己片面断开连接,Client发送的报文中就会携带FIN标志位。( 注意:Client片面断开连接以后,只是断开Client ->Server方向上的数据传输,此时Server可以给Client发送数据,反过来不行。)
- 第二次挥手,Server收到Client的通知哀求,并给Client发送应答报文,报文中携带ACK标志位。
- 第三次挥手,Server 通知 Client 自己片面断开连接,Server发送的报文中会携带FIN标志位。
- 第四次挥手,Client收到Server的通知哀求,并给Server发送应答报文,报文中携带ACK标志位。
4.RST RST表现复位标志位。代表着重新创建连接。
三次握手创建连接并不肯定可以大概成功创建连接,没人说三次握手肯定可以大概成功,同样四次挥手也一样,就算连接创建成功了,那也是有可能断开的,好比片面的将服务器主机电源拔掉,那连接不就会主动断开吗?
等服务器重启的时间,服务器不认为连接创建成功,但client还认为连接存在着,以是client就会给服务器不停发消息,服务器就会感觉很奇怪,连接都已经不存在了,你为什么还要和我通讯呢?
以是此时服务器就会给client发送一个复位报文段,其报头中的RST标志位被置为1,告诉client说,你别再给我发消息了,连接早就非常断开了,你再重新发起三次握手,重新和我创建连接吧。
以是复位标志位用于通讯双方中,任何一方认为创建连接不一致时,认为连接非常的一方会发送复位报文段,告知对方我们需要重新创建连接。
我们看几个例子
客户端与服务器通过三次握手成功创建连接,但是正常通讯时服务器端的操纵体系资源满载,导致服务器无法对客户端做出应答,由于服务端创建连接后也要管理连接,操纵体系描述管理这些连接数据结构,服务端OS为相识决资源满载的问题可能会释放掉创建的连接,服务端端必须重新发送RST标识为1的报文给对应客户端哀求重新创建连接才可以进行通讯。
就像下面这样子。
client在三次握手的时间,认为只要把三次握手中第3次报文发出,连接就创建好了!!!
但是要是真的第3次握手的时间失败了,就会是下面这种情况
5.PSH PSH表现敦促标志位,可以理解为Push。
Client在不停发数据,Server在不停接收数据同时在给Client发送应答,应答报文中包罗了16位窗口大小的字段,实在就是在告诉Client自己接收缓冲区剩余空间的大小,以便于Client实时调解自己的发送速度。
但是,Server接收缓冲区快满了或者说已经满了,此时Client给在发送的报文里设置PSH标志位来敦促对方尽快取走缓冲区的数据。(让对方上层应用步伐立刻把数据从TCP接收缓冲区读取,包管TCP接收缓冲区有能力接收新数据或清空TCP接收缓冲区)
增补:
缓冲区满了可能是因为Server应用层还在处置惩罚上一条数据,导致没偶然间调用read接口函数来取走缓冲区里的数据。
缓冲区存在低水位和高水位标志,OS 敦促上层取数据的依据便泉源于此,低水位就代表当前数据太少了,先别急着读;高水位就代表缓冲区里的数据太多了,赶紧来取走。
一般OS是让上层每次都读取一批数据,而不是每次只读取一个字节。因为上层是调用read接口函数来从缓冲区读取数据的,如果每次都只读取一个字节,那就需要频仍的调用read函数,也就需要频仍的在用户态和内核态之间切换,这样会低落服从。
6.URG
URG表现告急标志位。表现本报文中发送的数据(有效载荷)是否包罗告急数据:URG=1时表现有告急数据;当URG=1时,后续的16位告急指针字段才有效。
当发送方盼望一些数据尽快被接收方的上层拿到的时间,就需要用到这个标志位。通常需要搭配16位告急指针使用,要传递的告急数据混在普通数据里,16位告急指针指明了告急数据在普通数据中的详细位置。
URG的告急指针字段在这里
16位告急指针表现的是告急数据在有效载荷中的偏移量,TCP规定死告急数据只能有1字节,当URG标志位被置为有效时,告急指针就会派上用场,接收方在读取TCP报头之后,会首先从告急指针表现的偏移量处读取1字节的告急数据,然后再重新从有效载荷的起始位置读取完成剩余的数据,这1字节的告急数据我们一般称为带外数据。
实际上告急指针和URG标志位我们在99.99%的情景下都用不到,如果真要是用带外数据,可能运维的一些职员会用到,好比服务器现在压力非常大,可以发送1字节的带外数据用于扣问当前服务器的状态怎么样,有没有规复到康健状态,服务器可以返回1字节的带外数据,用1字节的数据来对应状态码,返回服务器是因为什么原因而导致过载,因为带外数据不用颠末冗长的数据流,可以直接在应用层读取。
以是带外数据实际上并不在正常的数据流中,一般用带外数据的也就是UDP和TCP协议了。如果想要读取带外数据,可以将recv的flags标志位按位或上MSG_OOB,这样就可以读取带外数据了。
这些标志位的组合和状态变革规则界说了TCP连接的创建、维护和关闭过程,以及在数据传输中的一些特定行为,确保了TCP连接的可靠性和稳定性。
3.TCP的可靠策略
3.1.确认应答机制
确认应答机制是发送方确保自己的数据要被对方收到。
发送方发出数据以后,接收方要给发送方一个复兴,确认自己已经收到数据了,这样的复兴称为ACK,确认应答报文。
而发送方收到确认复兴之后也知道自己的数据被对方收到了。
发送方可以同时发送多条数据,接收方根据收到的消息给出复兴,但存在“后发先至”问题,接收方收到的数据顺序被打乱,这可能引起严重的歧义。
比方下面这种情况,双方最终会造成误解。
针对“后发先至”问题,TCP的办理办法是给传输的数据和应答报文都进行编号。
TCP是面向字节省传输的,在编号的时间是给每个字节都编上序号,假设每次传输1000个字节,那么第一个字节的序号就是1,第二个字节的序号就是2...末了一个字节的序号就是1000。
由于这1000个字节是属于同一个TCP数据报的,TCP报头就只记载第一个字节的序号。
上述说到的编号就需要用到TCP报文格式中的32位序号和32位确认序号,发送方利用32位序号记载发送的数据的第一个字节的序号,接收方读完数据之后,返回给发送方32位确认序号,即告之对方下一个数据的第一个字节应该从哪个序号开始。
在TCP协议中,每一条数据都是有序号的,包罗应答报文,一条数据是否是应答报文就要根据ACK标志位来确定,如过ACK为1那就是应答报文了,如果为0则不是应答报文了。
TCP的可靠传输主要就是通过确认应答机制来包管的,通过应答报文就能让发送方清晰的知道传输是否成功,引入32位序号和32位确认序号进一步确保对多条数据有效的传输。
3.2.超时重传机制
我们得知道一台主机发了一条消息出去,他自己是不知道有没有发送成功的,是靠对方发回的回应知道自己上传发送的消息成功发送了的。
确认应答机制讨论的前提是每条数据都能顺遂传输的情况,那么在丢包(对方没有收到我发的信息或者我没有收到对方的复兴)的情况下该怎样应对呢,应对方法就是超时重传(隔一段时间重新发送)。
丢包存在两种情况,发的数据丢包了,返回的ACK丢包了,不管是哪种情况,只要没有收到ACK,都会在到达某个时间阈值之后进行重传。
数据丢包是一个概率变乱,假设一条数据在传输的过程中丢包的概率是5%,传输成功的概率是95%,那么第一次传输丢包,第二次重传也丢包的概率是5%*5%=0.25%,第三次重传也丢包的概率可以忽略不计,在实际情况中,丢包的概率是一个非常小的数字,而上述假设的5%已经是一个很大的数字了,如果连续多次重传照旧丢包的情况下,那么就要思量是否是网线断了或是其他情况了。
- 增补知识2——自己这里认为发出,但是没有收到对方复兴的消息会先保存到滑动窗口
我们知道发送的数据段是有可能没有收到ACK的,以是被发出的数据不应该立马被移除(计算机上的移除实在就是数据覆盖),应该先保存一段时间,如果发送的数据丢包了,则可以将保存的数据再重新发送,而像这样已经发送但没有收到ACK的数据,实在是存放在滑动窗口里面的,这个后面会讲,现在先提一下。
- ACK丢包的情况下,实在接收端已经收到发送端传输过来的数据,发送端再次发送相同的数据过来该怎样处置惩罚呢?
TCP有一个特别的处置惩罚功能,去重,TCP存在一个“接收缓冲区”的存储空间,接收端会将读到的数据放到对应的缓冲区,根据数据的序号,TCP就能识别是否有两条重复的数据,如果重复就把后面的这条数据给扬弃了。
- 特定的时间间隔怎么定呢?TCP是否会进行无穷次的重传?
实在这个时间应该是随着网络情况动态变革的,如果网络情况好,超时时间设定的非常长,这实在就会影响网络传输的服从,因为数据包发送的速度非常快,可能数据包往返一次共需要50ms,但你将超时时间设定为500ms,那中心的450ms的时间就会被平白无故浪费掉,如果网络情况特别差,超时时间设定的非常短,那更离谱了,数据包正在传输的过程当中就被判定为丢包了,这同样也会影响数据传输的服从。
以是最抱负的情况,就是找出一个最短的时间,包管绝大部分的网络情况下,数据包都可以在这个最短的时间窗口内,发送已往,同时ACK报文可以大概发回来。
在linux(unix和windows也一样)中,超时实际上是以500ms作为基本单元来进行控制的,如果第一次重发后,还没有得到确认,则会以2的指数幂×500ms的方式来逐渐增大超时的时间窗口,累计达到肯定重传次数,则TCP会强制关闭双方创建的连接。
3.3.连接管理机制
3.3.1.怎样理解TCP连接
事实上,我们说的TCP的三次握手创建的这个连接,实在是端到端的这个连接,也就是客户端的应用层到服务端的应用层的连接,只要我客户端的应用层没和服务端的应用层连接上,我们就可以说这个TCP连接是失败的。
当上层(如应用层)调用connect函数时,它实际上是哀求传输层(TCP协议层)来创建TCP连接。TCP协议层随后会进行三次握手过程,以在客户端和服务端之间创建连接。在这个过程中,SYN报文是由客户端的TCP协议层(传输层)发出的,用于发起连接哀求。
需要注意的是,固然我们说TCP连接是端到端的,但在实际的网络传输过程中,数据会颠末多个网络装备和协议层的处置惩罚。然而,这些处置惩罚对上层应用来说是透明的,它们只需要关注TCP连接是否成功创建,以及数据是否可以大概在应用层之间可靠地传输。
- 因此,我们在下面如果看到了,显着一方和另一方断开连接了(实质是它们之间的应用层断开连接),但是其他层还可能存在接洽,后面还能发SYN这类报头的,都是传输层在发的
我们知道,TCP 在『端对端』之间创建的信道,为上层『端』对应的历程提供服务,它由客户端和服务端的套接字(socket)以及它们之间交换的数据包(segment)构成。TCP 连接的创建、维持和停止都需要遵循肯定的协媾和状态机制。
别的,中心化的 Client-Server 模式使得大量不同的 Client 将会与同一台 Server 创建连接,那么 Server 端势须要对这些泉源不同的连接进行管理。
从数据结构的角度理解:我们知道,TCP 是处于传输层的协议,也就是说,TCP 的各种逻辑由操纵体系(特指 Linux)维护,那么这些数据就得按照操纵体系的规则组织,即先描述,后组织。
现在我们知道了,这些连接在操纵体系眼里,只不过内核中的数据结构类型(通过结构体组织),当连接成功被创建时,内存中就会创建对应的『连接对象』。管理不同的连接,即对这些连接对象进行增删查改等操纵。
既然组织连接相干的数据结构需要操纵体系维护,那么维护是需要本钱的,主要是CPU 和内存资源。这是许多网络攻击方式的切入点。
固然,TCP 连接需要维护一些状态信息和参数,比方序号、确认号、窗口大小、重传计时器等。这些信息和参数被存储在一个称为传输控制块(Transmission Control Block,TCB)的数据结构中。每个 TCP 连接都有一个唯一的 TCB 与之对应,操纵体系用一张表来存储所有的 TCB。TCB 中的信息和参数会随着连接的状态变革而更新。
最重要的原因就如刚才所说,两个具有代表性的协议:TCP 和 UDP 都是传输层的协议,而传输层由操纵体系内核维护,那么协议的实现必须符合操纵体系中的规则。
别的,在 Linux 中,传输控制块(Transmission Control Block,TCB)和线程控制块(Thread Control Block,TCB)或者历程控制块(Process Control Block,PCB)之间的关系是不同的,它们分别属于不同的条理(前者是传输层,后两者是内核),它们之间的接洽是:
- 一个历程可以创建多个线程,这些线程共享历程的资源,如内存空间、文件描述符等。因此,线程控制块中有一个指针指向所属历程的历程控制块。
- 一个历程或者线程可以创建多个套接字,这些套接字用于与其他历程或者线程进行通讯。因此,历程控制块或者线程控制块中有一个文件描述符表,其中包罗了指向套接字对应传输控制块的指针。
TCP是面向有连接的,
- 要在连接的情况下进行数据的传输,通常情况下要进行三次握手创建连接
- 断开连接时,要进行四次挥手断开连接。
3.3.2.连接——三次握手
用户端调用connect函数,然后操纵体系就会主动在内部完成3次握手
TCP是面向连接的协议,双方在正常通讯之前需要先创建连接,创建连接是一个三次握手的过程。
- 第一次,Client 给Server发送哀求连接,报文中携带SYN标志位来表明当前报文是和创建连接相干的。
- 第二次,Server接受Client的连接,并扣问何时创建连接,报文中也会携带SYN标志位,同时会携带ACK往返复上一条哀求。
- 第三次,Client 复兴 Server,立马创建连接,这里只是单纯的复兴,只要设置ACK即可。
有人可能会疑问,客户端最开始不是没和服务端创建连接吗?那客户端为什么能发送SYN给服务端呢?
事实上,我们说的TCP连接,实在是端到端的这个连接,也就是客户端的应用层到服务端的应用层的连接,只要我服务端的应用层没和服务端的应用层连接上,我们就可以说这个TCP连接是失败的。
当上层(如应用层)调用connect函数时,它实际上是哀求传输层(TCP协议层)来创建TCP连接。TCP协议层随后会进行三次握手过程,以在客户端和服务端之间创建连接。在这个过程中,SYN报文是由客户端的TCP协议层(传输层)发出的,用于发起连接哀求。
需要注意的是,固然我们说TCP连接是端到端的,但在实际的网络传输过程中,数据会颠末多个网络装备和协议层的处置惩罚。然而,这些处置惩罚对上层应用来说是透明的,它们只需要关注TCP连接是否成功创建,以及数据是否可以大概在应用层之间可靠地传输。
- 因此,我们在下面如果看到了,显着一方和另一方断开连接了(实质是它们之间的应用层断开连接),后面还能发SYN这类报头的,都是传输层在发的
那么问题来了,站在Client的角度,什么时间认为连接已经创建起来了呢?答案是第二次握手以后,即收到Server的复兴。
然而站在Server的角度,创建连接的时间是在第三次握手,因为第二次握手只是Server端片面同意了,Client端有没有同意是在第三次握手才知道。
这些包使用 TCP 首部用于控制的字段来管理 TCP 连接,创建一个 TCP 连接需要发送 3 个包,形象的称为“三次握手”。
注意:
- 图中固然以 SYN 等标志位哀求和应答(包罗下文常用标志位代替报文),但实际上两端交换的是报文,而不是标志位。报文可能携带数据,也可能只含有报头。理论上在创建连接时,每个报文都应该有回应(就像打电话一样),在奇数次握手中,末了一个报文在连接创建之前肯定没有回应的。
- 客户端和服务端都要向对方发送创建连接的哀求(SYN),而且需要接收到对方的确认应答(ACK)后,才能认为『这个方向』的通讯信道创建成功。这是因为 TCP 要实现『全双工』通讯,就必须要包管双方通讯的信道是流通的。
- 有的时间把这个创建连接的过程叫做“四次握手”,这是因为这种说法把第二次握手 ACK+SYN 拆分成了两次握手,实际上都是一样的。
有人想说,TCP只能包管汗青消息的可靠性,永远不谈最新一条消息的可靠性,这句话没问题,好比三次握手的末了一次握手,这个消息的确就是不可靠的,因为客户端发送的ACK报文段,server是否收到,客户端是不知道的!
但这没有关系,就算ACK报文段丢失了,那server不会认为连接创建成功,此时如果client给server发送消息,则server会感觉很奇怪,既然连接不创建成功,你还给我发消息,那就分析我们双方产生了认为连接创建不一致的情况,那server就会给client发送复位报文段,哀求重新三次握手,重新创建连接,因为我们现在连接的创建是不一致的,client认为连接创建成功,但server不认为成功。
或者另有另一种情况,server发送的SYN报文段超时没有确认应答,则server就会进行超时重传,当client收到重复的捎带应答报文段时,client就会心识到自己给server发送的确认应答报文段可能丢失了,此时client就会重发ACK报文段。
这是一个经典的问题,有许多不同的表明和角度。可以从几个方面往返答,在这里仅从服从角度讨论,在『再次理解“三次握手”中』会从多个角度回答这个问题。
如果只用 1 次,那么客户端发送一个 SYN 后就认为连接创建成功,但是如果这个 SYN 丢失了或者被延迟了,那么服务器端就无法知道客户端的哀求,也无法给客户端发送数据。除此之外,每次连接都会占用服务端肯定的 CPU 和内存资源,只用 1 次握手就认为创建连接成功,那么当服务端在短时间内接收到大量 SYN 连接哀求,会造成服务端非常,即 SYN 洪水攻击。
那么如果是两次握手就创建连接的情况下,当服务器将第二次的握手的信息发出去之后,默认连接已经创建,而且开辟空间资源等候接收数据。但如果第二次握手的信息丢包了呢,服务端认为二次握手已经完成,创建了连接,而客户端呢,由于什么也没有收到,以是会进行超时重传,当服务器又收到连接哀求,认为有新的客户端发起连接,于是同意连接哀求,并开辟空间资源等候接收数据,如果出现大量上述情况,便会造成服务端的崩溃。这种情况下,如果客户端重复地向服务端发送 SYN 哀求,也会造成服务端的 SYN 洪水。
a.三次握手可以大概以最小本钱验证全双工通讯。全双工通讯指的是server和client各自都可以大概进行数据的接收和发送,发送和接收是解耦的,client可以发送SYN报文段,也能接收来自server的ACK报文段,server可以发送SYN报文段,也能接收来自client的ACK报文段,话说回来,四次握手可以验证全双工通讯吗?固然也可以,但既然三次握手行,为什么要四次握手呢?第四次握手不是平白无故的消耗网络资源吗?以是三次握手是以最小本钱来验证全双工通讯的。
b.三次握手可以防止产生单主机对服务器SYN洪水攻击的毛病。
在三次握手中,server片面对client创建连接的前提是client已经片面向server创建好连接了。而客户端在服务端第一次返回SYN+ACK的时间,就client已经片面向server创建连接,而这需要消耗客户端内部资源。在这之后客户端向服务端发送ACK后,服务端才会消耗资源来维护连接。这样子连接失败的本钱嫁接到客户端上了。
这样 SYN 洪水攻击也就失效了,因为三次握手会让发出 SYN 的一方(即服务端)接收等量的 ACK 相应,而大部分情况下服务器的设置要比client高许多,以是如果双方在不停的以相同本钱进行消耗,那也肯定是client先扛不住,而不是server,以是单主机的情况下,client想要SYN洪水攻击服务器,这是不实际的!这样服务端就能负担最小程度的连接失败本钱。
前面我们说过三次握手已经可以最小本钱验证全双工通讯了,那四次,五次握手肯定也可以。
但四次握手也存在SYN洪水攻击的毛病,末了一次握手是server发送给client的,让client来创建连接的,但client可以忽略掉这个报文段,只让server自己创建连接,去负担维护连接需要的本钱。
同时五次握手由于末了一次是client发送给server的,以是server创建连接之前,client也会创建连接,双方是同等本钱的消耗,则可以制止单主机的SYN洪水攻击。
以是三次握手之后的其他握手,偶数次依旧存在SYN洪水攻击,奇数次不存在SYN洪水攻击,但他们都不是最小本钱验证全双工通讯信道,以是使用三次握手,而不是其他握手!
实在从理论上讲,只要末了一次是客户端发送一个 ACK 给服务器端就行(为啥?因为要嫁接连接失败本钱)就可以,即 5/7/9 次。
… 但是这样做没有须要,因为第三次握手已经足够包管双方的同步和确认信息了,再多发送一次或多次只会增加网络开销和延迟。也就是说,三次握手是验证双方通讯信道连接成功的最小次数。
TCP 的三次握手,主要是为了在包管连接可靠性和双向性的同时,尽量镌汰网络开销和延迟。
3.3.3.断开连接——四次挥手
创建连接是由一方主动发起,大部分都是客户端主动向服务器发起连接哀求,但断开连接可以由任意一方主动发起,发起的上层条件,实在就是调用close()关闭套接字文件描述符sockfd。
断开连接是一个四次挥手的过程。
Client 片面断开连接需要两次;Server端片面断开连接需要两次,加起来就是四次。
谁先断开连接,这个没有限定,下面就假设Client先断开。
- 第一次挥手,Client 通知 Server 自己片面断开连接,Client发送的报文中就会携带FIN标志位。( 注意:Client片面断开连接以后,只是断开Client ->Server方向上的数据传输,此时Server可以给Client发送数据,反过来不行。)
- 第二次挥手,Server收到Client的通知哀求,并给Client发送应答报文,报文中携带ACK标志位。
- 第三次挥手,Server 通知 Client 自己片面断开连接,Server发送的报文中会携带FIN标志位。
- 第四次挥手,Client收到Server的通知哀求,并给Server发送应答报文,报文中携带ACK标志位。
注意:所谓的TCP连接,实在是端到端的这个连接,也就是客户端的应用层到服务端的应用层的连接,只要我客户端的应用层没和服务端的应用层连接上,我们就可以说这个TCP连接是失败的。但是其他层还可能保持着接洽。
我们说断开连接,也就是断开客户端应用层和服务端应用层的连接。我们下面说断开连接之后,还能发ACK等信息,都是传输层(操纵体系)在发送。
我们这里所说的不发送消息,指的是不发应用层的数据了,并不代表传输层自己不能发送该层的管理报文段,比方FIN,ACK等等报文段。
可能有读者会敏锐的发现, 这里的 “四次挥手” 过程似乎与 “三次握手” 的过程差别不大, 三次握手可以将中心的 ACK 和 SYN 归并为一次, 为什么这里的四次挥手没有将 ACK 和 FIN 归并为一个步骤呢?
我们直接公布答案, 四次挥手有的时间确实可以三次完成, 但是大部分时间都是分为四次完成的, 因为中心这两次挥手过程, 不肯定可以大概被归并.
与握手不同, 三次握手的过程都是在内核中, 体系自行控制的, 可以说是中心的 SYN 和 ACK 是瞬发的, 而我们四次挥手时发送的 FIN 则是由用户代码来控制的. 只有调用 close 方法, 或者用户历程结束, 才会触发 FIN, 相比之下, ACK 是由内核控制, 第一次收到 FIN 时, 就会立马返回 ACK.
未完待续……
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |