网络原理 | TCP协议的常见核心机制

目录

确认应答机制

超时重传机制

连接管理机制

滑动窗口机制

流量控制机制

拥塞控制机制

延时应答机制

捎带应答机制


确认应答机制

确认应答机制即接收方在收到数据后,就要给发送方返回一个"应答报文"(ack,acknowledge).

TCP通过引入序号和确认序号使得应答报文和传输的数据对应上。由于 TCP 是面向字节流的,此处的编号不是按照"一条两条"来编号,而是按照"字节"编号的,对于 TCP 数据报来说,知道了数据部分的第一个字节序号,就知道了后续所有字节的序号。序号仅针对 TCP 数据报携带的载荷来进行编号(TCP)报头不参与编号。也就是说 TCP 报头的序号,描述的是 TCP 数据报载荷部分第一个字节的序号是多少。

32位/4字节序号号是一个 32 位无符号整数,表示的范围是从0 -> 42亿9千万,即0 -> 4GB。TCP传输数据的基本单位是字节流,一个数据报和下一个数据报是可以拼接的,此处的数据报是指 TCP 报头 + TCP 载荷。如果要传输一个特别大的数据,传输过程中通过多个 TCP 数据报来携带数据,这些 TCP 数据报携带的载荷在接收方会被自动的拼接起来。

当传输的数据总量超过 4GB 时,序列号会回绕到 0 重新开始。例如,传输 8GB 文件时,序列号会经历两次完整的循环。为防止回绕导致报文混淆,TCP 使用时间戳选项(Timestamp)和 PAWS 机制来区分不同周期的相同序列号。

TCP的序号是按照字节来进行编排的,如下图所示:

确认应答机制如下图所示:

主机 A 给主机 B 发送序号为1-1000的数据,当主机 B 收到这个数据后,会将确认序号设置为1001 返回给主机 A,告诉主机 A 序号 < 1001 的数据都已经收到,下一次主机 A 发送数据时,就会从序号 1001 开始。主机 B 返回给主机 A 的报文称为应答报文,对于应答报文来说,确认序号会按照接收到的数据的最后一个序号 + 1 的方式来填写,另外会将 6 个标志位中的 ack 设为1,表示该报文是应答报文。对于普通报文 ack 为 0,其序号是有效的,确认序号是无效的;应答报文 ack 为 1,其序号和确认序号都是有效的。

在网络传输中,数据报可能因为抖动、拥塞、重传等原因,导致先发送的包后到达,这样的问题称之为后发先至。通过确认应答机制可以解决网络传输过程中存在的后发先至问题。例如主机 A 给主机 B 发送数据,如下图所示。

由于网络传输中的问题,数据报 1001-2000 比数据报 1-1000 先到,主机 B 调用 read 没有读到1 - 1000 这个数据就会阻塞等待,虽然 1001-2000 这个数据到了,但主机 B 不会让 read 解除阻塞,虽然读到了 1001-2000 这个数据,但还是要继续阻塞等待,直到读到 1-1000 这个数据之后才会接触阻塞等待,进而继续读取1001-2000 和 2001-3000,确保发送方 write 的数据和接收方 read 的数据一样。

超时重传机制

在网络传输的过程中,经常会出现丢包问题,常见的原因如下:

1.数据传输过程中,发生了 bit 翻转。收到这个数据的接收方/中间路由器等计算校验和,发现校验和对不上,此时就会将这个数据报丢弃掉,不继续转发。

2.数据传输到某个节点(路由器/交换机)时,这个节点的负载太高了(某个路由器,单位时间内只能转发 N 个包,在网络高峰期,这个路由器单位时间需要转发的包超过了 N,后续传过来的数据就可能被这个路由器直接丢掉),这样也会发生丢包情况。

在 TCP 中,通过确认应答机制来判断是否丢包,如果发送方收到了应答报文就说明没有丢包,反之则发生丢包。发送方发送数据之后,会给出一个时间限制(超时时间),如果在这个时间限制之内发送方还没有收到 ack,就视为数据丢包了。

丢包的情况大概可以分为以下两种情况:

· 第一种情况:主机 A 给主机 B 发送数据之后,可能因为网络拥堵等原因导致数据无法到达主机 B.

**·**第二种情况:主机 A 发送数据给主机 B 的数据成功到达主机 B,但是主机 B 在给主机 A 返回的应答报文由于网络拥堵等原因无法到达主机 A,这也是一种丢包情况。

超时重传机制即如果主机 A 在一个特定的时间间隔内没有收到主机 B 发来的确认应答,就会进行重发。然而, 主机 A 无法判别是以上两种情况中的哪一种,如果是主机 B 返回的 ack 丢失,那么主机 A 重新给主机 B 发送数据成功到达主机 B 后,此时主机 B 中就有两份相同的数据了。对于这种情况,TCP 针对这种情况做了处理,以保证识别出重复的包并将其去除。

接收方会有一个接收缓冲区,收到的数据都会先放在缓冲区中,后续再收到数据就会根据序号在缓冲区中找对应的位置(排序),如果当前序号的数据在缓冲区中已经存在,就直接将这个新收到的重复数据丢弃掉,以确保应用程序在调用 read 读出来的数据是唯一的、不重复的。

超时时间的设置不是固定的,是动态变化的。当发送方第一次重传时,超时时间是 t1.如果重传之后仍然没有收到接收方发回的 ack 报文,就会在经过超时时间 t2 后进行第二次重传,其中 t2 > t1,每多重传一次,这个超时时间的间隔就会变大,重传的频次会降低。

每经过一次重传,都会使数据到达对方的概率提升很多,如果重传几次后都没有收到接收方返回的应答报文,说明网络的丢包率很高,也就是说网络发生了严重故障,大概率不能继续使用。

TCP 的重传也不会一直进行,当重传达到一定次数后,TCP 不会继续尝试重传,也就可以认为这个连接已经断开了。TCP 会尝试进行"重置/复位 连接",发送一个特殊的数据包 "复位报文" 给接收方,如果此时网络恢复了,复位报文就会重置连接,此时双方的通信就可以继续进行。如果网络还是有严重问题,复位报文也没有得到回应,此时 TCP 就会单方面放弃连接。连接就是通信双方各自保存对方的信息,发送方释放掉之前保存的接收方的相关信息,这个连接也就断开了。

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

确认应答机制和超时重传机制相互补充,共同构建了 TCP 的可靠传输。

连接管理机制

连接管理机制即通过三次握手建立连接,通过四次挥手释放连接。这里的次数是指网络通信的次数,在建立连接和释放连接的过程中,发送的都是不携带业务数据(没有载荷,只有报头)的数据包。

三次握手建立连接

建立连接是一个双向操作,首先客户端向服务端发送一个 syn 请求建立客户端到服务端的连接,通俗一点说就是我想与你建立连接(即客户端想保存服务端的信息);在服务端收到来自客户端的 syn 后,会返回给客户端 syn 和 ack,返回的 ack 表示同意建立客户端到服务端的连接,syn 表示请求建立服务端到客户端的连接(即服务端想保存客户端的信息);客户端在收到服务端发来的 syn 和 ack后,此时客户端到服务端的连接成功建立,此时客户端会给服务端发送一个 ack,表示同意建立服务端到客户端的连接,当服务端收到来自客户端的 ack 后,此时服务端到客户端的连接也成功建立,此时双方之间的连接建立成功了。TCP 的连接建立本来是四次交互,但是将中间的两次交互合并了,就成了三次握手,如果分两次发送效率会变低,因为每个数据报都需要一系列的封装分用。

三次握手的意义

(1)初步验证通信的链路是否畅通;

(2)确认通信双方各自的发送和接收能力是否正常(①客户端给服务端发送ack后,不知道自己的发送和接收能力是否正常;②服务端收到客户端发送来的 ack 后,服务端这边就确认了自己的接收能力正常,客户端的发送能力正常,服务端给客户端返回 ack + syn,此时服务端不确定自己的发送能力是否正常;③客户端收到服务端返回的 ack + syn 后就知道了自己的发送能力和接收能力正常,且知道了服务端的接收能力和发送能力正常,客户端返回给服务端 ack;④服务端收到客户端返回的 ack 后,就确定了自己的发送能力也正常。此时通过三次握手就确定了双方各自的发送和接收能力都正常);

③在通信之前,对通信过程中的一些关键参数进行协商确定,比如 TCP 连接中的起始序号。

四次挥手释放连接

此处的四次挥手释放连接与前述的超时重传中的断开连接是有区别的,前述的是单方面的断开连接,此处的释放连接是双方各自把保存的对端信息删除。断开连接不一定是客户端主动发起的,也可能是服务端主动发起的。下面以客户端主动发起断开为例:

①首先,客户端向服务端发送一个 FIN 报文,表示请求断开客户端到服务端的连接;②服务端收到客户端发来的 FIN 报文后,会返回给客户端一个 ACK 应答报文,确认客户端的断开请求,客户端收到这个应答报文后,此时客户端处于半关闭状态(即客户端不再发送数据,服务端可继续发送数据)。由于此时服务端可能仍有数据要给客户端传输,因此不能立刻给客户端发送 FIN 报文;③当服务端给客户端的数据发送完毕后,此时会给客户端发送一个 FIN 报文,表示请求断开服务端到客户端的连接;④客户端收到服务端发送的 FIN 后进入 TIME_WAIT 状态,并返回给服务端一个 ACK 应答报文;⑤服务端收到 ACK 后就会立即关闭服务端到客户端的连接,进入 CLOSED 状态,客户端等待 2MSL 后自动进入 CLOSED 状态,至此客户端也完成了连接的关闭,双方之间的连接释放。

三次握手之所以是三次,是因为中间两次的交互合并到了一起,对于四次挥手来说,中间两次交互大概率是不能合并在一起的。因此三次握手中间的两次交互 ACK 和 SYN 都是在操作系统内核中,由操作系统负责进行的,都是处在同一时刻的,因此可以合并。而对于四次挥手来说,ACK 是由操作系统内核控制的,但是 FIN 的触发是通过应用程序调用 close() 或者进程退出来触发的。

TCP的关键状态

· LISTEN:服务器进入的状态。当服务器绑定好端口后,就相当于进入了 LISTEN 状态,此时服务器就已经初始化完毕。

· ESTABLISHED:客户端和服务端都会进入的状态。客户端和服务端都进入这个状态表示 TCP 的连接建立完成(双方都保存了对方的信息),此时就可以进行业务数据的通信了。

· CLOSE_WAIT:被动断开连接的一方,会进入到这个状态,即先收到 FIN 的一方,这个状态表示等待代码执行 close 方法。如果发现服务端出现大量的 CLOSE_WAIT 说明服务端的程序没有调用 close 函数关闭连接。

· TIME_WAIT:主动断开连接的一方会进入这个状态。此处的 TIME_WAIT 按照时间来等待,等待一定时间后连接就会释放了。

TIME_WAIT 的存在是为了确保被动关闭连接的一方可以正确的关闭。因为在网络传输的过程中存在丢包的情况。客户端在收到服务端的 FIN 后,会给服务端发送一个 ACK,如果这个 ACK 丢包了,服务端会重新给客户端发送一个 FIN,客户端这边也需要再发一次 ACK,能够再发一次 ACK 的前提是客户端这边的连接还不能释放(如果释放了,就不知道对方的信息了,也就无法返回任何数据了)。如果没有 TIME_WAIT 这个状态,客户端在收到服务端的 FIN 后直接进入 CLOSED状态,此时如果发生 ACK 丢包的情况,服务端这边的链接就不能正确的关闭了,与 TCP 可靠传输不符合。

TIME_WAIT 存在的时间是 2MSL(MSL即数据报在网络传输中消耗的最大时间),2MSL 的时间是从客户端收到 FIN 发送 ACK 开始计时的,如果在 TIME_WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到服务端重发的 FIN 报文,此时 2MSL 的时间会重新计时。在Linux 系统中 2MSL 默认是 60s。

滑动窗口机制

TCP 的确认应答机制针对每一个发送的数据段都需要返回一个 ACK 确认应答。收到 ACK 后再发送下一个数据段,这样做虽然保证了 TCP 的可靠传输,但降低了传输的效率。TCP 为了在可靠传输的基础上,也能够有一个不错的效率,因此引入了滑动窗口机制。

滑动窗口机制传输数据的方式如下所示:

滑动窗口机制即将 "发送一个数据等待一个确认应答报文 ACK" 改成 "发送一批数据等待一批确认应答报文 ACK"。这样做将多次等待 ACK 的时间合并成了一份时间,批量发送的数据越多,可以认为网络传输的效率就越高。窗口大小即无需等待确认应答就可以发送数据的最大值,这里指的是批量发送数据的字节数。窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区删除。

如下图所示,窗口的大小是4000,批量发送1001 - 5000这组数据,当主机B收到主机A发来的数据后,会返回给主机 A 2001的ACK,当主机 A 收到 2001 ACK时,说明1000-2001 数据已经到达了主机 B,此时就会立即发送 5001 - 6000 的数据,此时等待的ACK 范围就是2001 - 6000(四份数据),窗口大小还是4000,窗口的大小不变,只是位置变了。

滑动窗口的前提是保证可靠性,如果在网络传输过程中出现丢包情况,应该如何处理呢?

此处可以分为两种情况进行讨论:一种是数据报到达了主机 B,但是主机 B 返回的 ACK 丢包了;另一种情况是主机 A 发送给主机 B 的数据报在网络传输过程中丢包了。

针对第一种情况,数据报到达了但是 ACK 丢包了。这种情况下 ACK 丢包了不会有太大影响,因为可以通过后续的 ACK 进行确认。确认序号,表示的是收到的数据的最后一个字节的下一个序号,也就是说如果主机 B 返回给主机 A 了一个确认序号,说明这个确认序号之前的数据主机 B 都已经收到,接下来主机 A 根据确认序号继续发数据即可。

例如下图中的情况,虽然 1001 ACK 丢包了,但是 2001 ACK 到达了主机 A,说明 2001 之前的数据主机 B 都已经收到了,接下来从 2001 开始发就可以了,后一个 ACK 可以涵盖前一个 ACK 的意义。

针对第二种情况:主机 A 发送给主机 B 的数据报在网络传输过程中丢包了。

如下图所示,当 1001-2000 这个数据报丢包后,主机 B 会一直给主机 A 发送 1001 ACK,表明我想要接收到的数据的起始序号是 1001,当主机 A 连续收到三次同样的 1001 ACK 确认应答后,就会将 1001-2000 这个数据报重新发送给主机 B,这个时候如果主机 B 收到了主机 A 发来的1001-2000 这个数据报后再次返回给主机 A 的确认报文就是 7001 了,因为 2001-7000 的数据主机 B 之前已经收到了,被放到了接收端主机 B 操作系统内核中的接收缓冲区中。

上述的过程快速的识别出是哪个数据报丢包了,并且针对性的重传,其他顺利到达的数据都无需重传,这个过程称为快速重传。快速重传搭配滑动窗口,确认应答搭配超时重传,当网络中通信双方单位时间内发送的数据量比较少,就按照确认应答和超时重传;如果单位时间内发送的数据比较多,就按照滑动窗口和快速重传。

流量控制机制

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区(每个 Socket 对象都会有一个内置的缓冲区)被打满, 这个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等⼀系列连锁反应.因此 TCP 支持根据接收端的处理能力来决定发送端的发送速度,这就是流量控制机制。

TCP 通过接收缓冲区的大小来制约发送方的发送速度,当接收方收到数据的时候就会将接收缓冲区的剩余空间大小通过 ACK 数据报反馈给发送方,下一步发送方就可以针对这个数据来设置发送的窗口大小了。

TCP 报文中的 16 位窗口大小体现了上述的接收方接收缓冲区的剩余空间,这个属性只有在 ACK 报文中才有效(也就是 ACK 这位等于 1 时有效)。此处的 16 位表示的范围是 0-64kb,但不意味着发送方窗口大小最大就是 64kb,因为在 TCP 报文的选项中可以设置一个特殊的选项"窗口扩展因子",发送方的窗口大小 == 窗口大小 << 窗口扩展因子。"<<" 即左移运算,左移一位相当于 * 2,比如窗口大小是 100,窗口扩展因子是 2,则发送方的窗口大小就是 400。

流量控制机制如下图所示,接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 窗口大小 字段,通过 ACK 告知发送端;窗口大小字段越大,说明网络吞吐率越高;接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知发送端;发送端接收到这个窗口后,就会减慢自己的发送速度;如果接收缓冲区满了,就会将窗口置为 0 。

当过了超时重发时间以后还没有收到窗口的更新通知,发送端就会给接收端发送一个窗口探测的包,这个数据报不携带业务数据(载荷部分是空的),只是为了触发接收端的 ACK 应答报文,通过这个来查询接收端接收缓冲区的剩余空间。

接收端也会在接收缓冲区不为 0 的时候(消费了一定数据后)主动触发一个 "窗口更新通知"这样的数据报给发送端。

拥塞控制机制

流量控制是避免发送方的数据填满接收方的缓存,但并不清楚网络中发生了什么。流量控制是站在接收方的视角来限制发送方速度的,而拥塞控制是站在传输链路的视角来限制发送方速度的。

拥塞控制机制将中间传输的节点视为一个整体,不关心其内部细节。其大致思想是一开始按照一个比较小的速度发送数据;如果数据传输的十分流畅,没有丢包情况出现,说明网络上传输数据整体是比较畅通的,此时就可以加快传输数据的速度;当增大到一定的速度之后,发现出现丢包情况,说明网络整体上存在拥堵了,此时就减慢传输数据的速度;减速之后发现不丢包了,此时又会继续加速;如果加速后又出现丢包的情况就继续减速。

流量控制和拥塞控制都会限制发送窗口,这两个机制会同时起作用,最终实际的发送窗口大小取决于上述两个机制得到的发送窗口较小值

如果发送方在规定时间内没有接收到接收方返回的 ACK 应答报文,也就是发生了超时重传,此时就会认为网络出现了拥塞。

拥塞控制主要是四个算法:慢启动拥塞避免拥塞发生快速恢复

· 慢启动:TCP 在刚建立完连接后,首先有一个慢启动的过程,一开始以一个很小的拥塞窗口来传输数据,一点一点的提高发送数据包的数量。发送方没收到一个 ACK 应答报文,拥塞窗口的大小就会加 1。慢启动算法,窗口的大小是指数级增长的,当增长到一定程度到达阈值(即慢启动门限ssthresh,slow start threshold)后就会使用拥塞避免算法。当达到阈值后,就算没有丢包,也会自动触发拥塞避免算法,以避免太快的发生丢包的情况。

**·**拥塞避免:当拥塞窗口超过慢启动门限(ssthresh)就会进入拥塞避免算法。当进入拥塞避免算法后,每收到一个 ACK,拥塞窗口增加 1 / 当前窗口大小,也就是线性增长。拥塞避免算法就是将原本慢启动算法的指数增长变成了线性增长,但还是处于增长阶段,只是增长速度减慢为线性增长。线性增长,也会使发送速度越来越快,当增长到一定程度后也会进入网络拥塞的状况,此时就会出现丢包情况,此时就需要对丢失的数据包进行重传。触发了重传机制后就进入了拥塞发生算法。

· 拥塞发生:当网络拥塞情况出现,发生数据包重传时,重传机制主要有超时重传快速重传,这两种使用的拥塞发送算法是不同的。

当发生超时重传后就会使用拥塞发送算法,此时,首先将慢启动门限ssthresh 设为当前拥塞窗口大小的一半,再将拥塞窗口大小重置为初始值,之后重新启动慢启动,先指数增长再线性增长。

当发生快速重传也会触发拥塞发送算法,但与上述情况完全不同。当接收方发现丢了中间一个包时,发送三次前一次包的 ACK,于是发送方会快速地重传丢失的包,不需要等待超时重传。此时,TCP 首先会将窗口大小设置为当前窗口大小的一半,之后再将慢启动门限设置为修改后的窗口大小,之后进入快速恢复算法。

**·**快速恢复:快速恢复和快速重传算法一般同时使用,进入快速恢复算法之前,拥塞窗口大小和慢启动门限ssthresh已经更新了,进入快速恢复算法后,首先将拥塞窗口大小 + 3(因为此时已经收到了 3 个重复的ACK);之后重传丢失的数据报,如果发送端再收到重复的 ACK ,则拥塞窗口大小再 +1;如果之后收到新数据的 ACK,就将拥塞窗口大小设置为慢启动门限ssthresh大小,因为当发送端接收到新数据的 ACK,说明丢失的数据报接收端都已收到,该恢复过程已经结束,就可以恢复到之前的状态,再次进入拥塞避免状态。

流量控制机制和拥塞控制机制都是对"可靠传输"的补充。

延时应答机制

延时应答机制是提升效率的机制,尽可能地降低可靠传输带来的性能影响,其提升性能的方式就是让滑动窗口大小变大。

如果接收方接收到数据后立即返回 ACK 应答报文,这时候返回的窗口大小可能就比较小,在流量控制机制中,使用接收方的接收缓存区的剩余空间大小来衡量窗口大小。例如,接收方缓冲区的大小为 100K,假设一次收到了 50K 的数据,如果立刻应答,此时返回给发送方的窗口大小就是50K;但是可能接收方处理数据的速度很快,2ms 就将这 50K 的数据从接收缓冲区中处理了,此时即使窗口再大一些,接收方也能处理过来数据;如果接收方稍微等一会再应答,比如等待 4 ms再应答,那么返回的窗口大小就是 100K 了。

窗口越大,网络的吞吐量就越大,传输效率就越高,TCP 再保证网络不拥塞的情况下尽可能地提高传输效率。通常情况下,每个 N 个包应答一次,超过最大延时时间就应答一次。不同的数量和超时时间在不同操作系统中也有一定的差异,通常 N 取 2,超时时间取 200ms。

捎带应答机制

捎带应答机制和延时应答机制一样,都是 TCP 提升性能的机制。捎带应答机制是在延时应答机制地基础上引入的提升效率的机制,其将返回的业务数据与 ACK 合并在一起返回给发送端。

网络通信中,大部分情况都是"一问一答"的形式,如下图所示:

ACK 是由系统内核返回的,在服务端收到客户端的请求后,就会立即返回 ACK。响应则是应用程序返回的,在代码中通产根据请求计算得到响应,再将响应写回给客户端。

正常情况下,ACK 和响应是不同时机发生的,无法合并,但由于 ACK 涉及延时应答机制,延时应答机制会使 ACK 的返回时间后延,这样延时之后就可能会赶上接下来应用程序返回响应数据的操作了,于是可以再发送响应数据的时候将 ACK 的信息也捎带上,这就是捎带应答机制。

ACK 报文本身不需要载荷,报头中设置 ACK 为1,设置窗口大小的值,设置确认序号等等,响应数据主要是设置载荷,因此其和 ACK 不冲突,是可以共存的。

相关推荐
暮色_年华5 小时前
《TCP/IP协议卷1》第3章 IP协议
网络
galaxylove5 小时前
Gartner发布新的网络安全运营模型:决定安全运营的9个组件
网络·安全·web安全
老六ip加速器6 小时前
获取ip地址安全吗?如何获取静态ip地址隔离ip
运维·网络·智能路由器
领世达检测V133529092496 小时前
蓝牙戒指欧盟EN 18031网络安全认证详细解读
网络
王火火(DDoS CC防护)6 小时前
高防 IP 是如何帮助数藏行业防刷的
网络·网络协议·tcp/ip
神仙别闹7 小时前
基于 Matlab 和 Truetime 的网络控制系统仿真
网络·matlab·php
Johny_Zhao1 天前
CentOS Stream 8 高可用 Kuboard 部署方案
linux·网络·python·网络安全·docker·信息安全·kubernetes·云计算·shell·yum源·系统运维·kuboard
浪剑超1 天前
https说明
网络协议·http·https
uyeonashi1 天前
【QT系统相关】QT网络
开发语言·网络·c++·qt