【计算机网络】TCP协议

news2024/11/14 19:49:42

文章目录

  • 1. TCP报文的结构
  • 2. TCP的发送缓冲区和接收缓冲区
  • 3. 确保可靠性
    • 序列号和确认序列号
    • 确认应答
    • 超时重传
    • 连接管理
      • 1️⃣三次握手建立连接
      • 2️⃣四次挥手断开连接
  • 4. 提高性能
    • 流量控制
    • 滑动窗口
    • 拥塞控制
    • 延迟应答
    • 捎带应答
  • 5. 面向字节流
  • 6. TCP/UDP对比


概念:TCP(Transmission Control Protocol)是一种面向连接的、可靠的、面向字节流的传输层协议,它用于在计算机网络中实现端到端的可靠数据传输。

1. TCP报文的结构

在这里插入图片描述

数据就是发送方发给接收方的资源(应用层报文数据),一个TCP报文也可以不携带数据而只有报头,因此TCP报头(Header)比较重要。

📝各个Header字段的作用如下:

  1. 源端口号与目的端口号:表明报文是从哪个进程来,到哪个进程去的;
  2. 序列号与确认序列号:与TCP确认应答机制强相关,后面详谈;
  3. 首部长度:表示该TCP首部有多少个4字节(该字段只有4个比特位,即范围在0~15,首部长度*4表示包括选项的整个报头长度,因此报头长度范围在20~60字节);
  4. 窗口大小:反映发送端目前的接收数据能力;
  5. 标志位:6个不同的标志位,标识TCP报文的类型(置1标志位有效);
    • URG:当前报文为紧急报文,搭配紧急指针使用
    • ACK:当前报文为确认应答报文,搭配确认序列号使用
    • PSH:通知接收端应用层立即将该报文数据从缓冲区中取走,“插队”
    • RST:复位报文段,通知接收端重新建立连接
    • SYN:同步报文段,请求与接收端建立连接
    • FIN:结束报文段,通知接收端本端即将关闭,需要断开连接
  6. 保留位:暂未设置功能,保留备用;
  7. 校验和:发送端填充,CRC校验。接收端校验不通过,则认为数据有问题。此处的检验和不光包含TCP首部,也包含TCP数据部分;
  8. 紧急指针:一个偏移量,通过紧急指针能够找到正文数据中的带外数据(紧急数据);
  9. 选项:用于在TCP连接的建立和维护过程中进行配置和协商(报头前20字节是定长的,选项是变长的,范围在0~40字节)。

2. TCP的发送缓冲区和接收缓冲区

在应用层,用户建立以TCP协议为基础的socket套接字,并使用系统调用write/sendread/recv进行数据的发送和接收。那么,用户调用系统调用收发的数据,是从网络的对端直接收发的吗?不。实际上,当一台主机建立了一个TCP连接,其OS中会同时存在一个发送缓冲区和一个接收缓冲区(由socket打开文件句柄维护),当应用层调用write/send,是向发送缓冲区中拷贝数据,当应用层调用read/recv,是从接收缓冲区拷贝数据。 OS负责将发送缓冲区中的数据送到网络中,将网络中的数据收到接收缓冲区中。底层的网络传输是由传输层以下的网络层、数据链路层等负责的,传输层只负责将数据发送给和接收到正确的端口(进程)。

在这里插入图片描述

补充:

  1. 发送缓冲区和接收缓冲区中,存储的都是一个个加了Header的TCP报文(OS自动添加报头和解除报头),而不是应用层的原始数据。

  2. 发送缓冲区和接收缓冲区都只用于缓存应用层数据,一些TCP报文可以不进入缓冲区,直接由OS负责发送和接收,如:不带数据的ACK,SYN,FIN报头。

  3. 因此,TCP通信是全双工的,可以同时发送数据和接收数据,不会相互干扰。


3. 确保可靠性

要确保协议在网络传输中的可靠性,就必须知道哪些情况是不可靠的,再来解决这些不可靠的情况。网络传输中的不可靠情况有以下几种:

  • 丢包:大量丢包和少量丢包
  • 乱序:报文的发送顺序和接收顺序不一致
  • 重复:接收端收到来自发送端的报文相同,浪费额外成本

序列号和确认序列号

💭为了保证可靠性,TCP引入了确认应答机制。确认应答机制需要与TCP报头中的序列号、确认序列号和标志位ACK搭配使用,因此需要先了解序列号和确认序列号。

  • 序列号(Seq Number)

    TCP中每一个字节都进行了编号,TCP的数据存在于发送缓冲区和接收缓冲区中,可以把这两个缓冲区看成是两个以字节为单位的数组,字节编号就是数组下标。由于TCP传输面向字节流,发送端发送的一个报文就是发送缓冲区中的一段以字节为单位的序列,该报文的序号(又称序列号)就是这段字节中第一个字节的编号。

    在这里插入图片描述

    🔎假设1001-2000字节是一个报文,则该报文的序列号为1001

  • 确认序列号(ACK Number)

    每个ACK应答都会有一个有效的确认序列号,以告知发送端下次期待接收的数据序列号。 确认序列号等于已成功接收到的数据的最后一个字节的序号加一。比如收到的报文是1001-2000字节(序列号是1001,大小由OS自动确认),那么确认序列号就是2001,告知发送端的信息是:2001之前的字节都已经收到了,下次请从2001号字节开始发送。注意,只有ACK标志位被置为1,确认序列号才有效。

确认应答

TCP发送端向接收端发送一个报文,该报文如果在网络中丢包,接收端就无法正确地收到完整的TCP报文了。因此,为了解决丢包问题,发送端必须先得知是否发生了丢包,确认应答机制保证了发送端能够获知报文发送的状态: 发送端向接收端发送一个报文,若接收端成功收到报文,则返回一个应答(应答就是ACK标志位被置为1的TCP报文),以告知发送端接收端已经成功收到报文了,没有丢包或其它问题发生;若接收端没有收到报文,则不会返回应答。

在这里插入图片描述

站在接收端的角度,已经告知了发送端它发送的报文是否被接收。而站在发送端的角度,对于应答的接收有两种情况。

  1. 成功收到应答。表示发送端的报文成功被接收端收到,发送端可以继续发送下一个报文。
  2. 没有收到应答。这种情况还有两种子情况:因为应答也是一个报文,它也会丢包,所以如果发送端没有收到应答,一种情况是发送的报文接收端压根没收到,那肯定也不会发回应答;另一种情况是接收端收到了报文也发回应答,但是应答丢包了,此时发送端也收不到应答。后者虽然报文没有丢包,但是发送端并无法确定。因此,如果发送端没有收到应答,统一认为是发送报文丢包,则执行超时重传机制(后面详谈)

在这里插入图片描述

序列号除了在确认应答中发挥作用,还有其它两个重要的作用:

  1. 保证报文的发送顺序与接受顺序相同。 报文在网络中传输会受到多种因素的影响:发送路径、网络拥塞情况等,这可能会导致报文从发送端发出的发送顺序与接收端接收到的顺序不同,这是万万不可的。因为TCP是面向字节流传输,在发送端一个完整的应用层报文可能会被组织在不同的TCP报文中发出,然后在接收端重新将这些TCP报文拼接起来。只有保证报文按序到达,才能成功完成拼接,使接收端与发送端看到相同顺序的字节流数据。接收端的传输层会对一个连接收到的报文按序列号排列后再输入接收缓冲区。
  2. 去除重复报文。 当发送端超时重传时,并不能保证接收端曾经没有收到过重传的报文,可能当前重传的报文已存在于接收端中,因此接收端可能会受到很多重复的报文。为了防止空间浪费,接收端会根据序列号进行比对,去除重复的报文。

超时重传

⏱发送端发出一个报文后,会等待一段时间,等待接收端的应答,时间一到,若未收到应答,则表示确认应答失败,此时发送端需重新发送该报文,这就是超时重传机制(RTO, Retransmission Timeout)。

💭那么,如何确定超时重传的时间呢?

最理想的情况是,找到一个最小时间,保证正常情况下(不出现丢包)确认应答一定能在这段时间内到达。但是这个时间难以准确设定,不同的网络环境,注定了传输速度的不同,时间也有长有短。若时间设置过长,会导致重传效率降低 (丢包早就发生了,还一直不重传);若时间设置过短,可能会在发送报文或确认应答尚未到达目标端就触发重传,导致频繁发送重复的报文。

因此,TCP为了确保无论在任何网络环境下都保证较高的通信效率,会不断动态更新这个最大超时时间。

在Linux中,超时时间以500ms为单位,初始时为500ms,若第一次重传失败(重传后仍没有收到应答),则将超时时间改为2*500ms,若第二次再次失败,再变为4*500ms,以此类推,以指数形式递增。累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接,这种情况是异常断开连接

连接管理

TCP是面向连接的,即两个网络进程在通信之前,必须先建立连接。TCP采用三次握手的方式建立连接。

🔗连接本质上就是在OS传输层中组织并管理的一种数据结构,用于保存连接的本地ip和端口号、对端ip和端口号、协议、连接状态等信息,netstat 指令可以查看网络状态,即查看本地维护的连接。一个已建立的TCP连接与对应的套接字socket相关联。

netstat命令使用指南

语法:netstat [选项]

常用选项:

  • n 拒绝显示别名,能显示数字的全部转化成数字
  • l 仅列出有在 Listen (监听) 的服务状态
  • p 显示建立相关链接的程序名
  • t (tcp)仅显示tcp相关选项
  • u (udp)仅显示udp相关选项
  • a (all)显示所有选项,默认不显示LISTEN相关

1️⃣三次握手建立连接

客户端与服务器通信前三次握手建立连接的过程图示:

在这里插入图片描述

⭕三次握手的过程:

  1. 服务器端通过socket, bind, listen三板斧后,获得一个LISTEN状态的连接,用于监听Client端的连接请求。
  2. 第一次握手: 客户端向服务端发送同步报文段(SYN标志位置1的报头),此时客户端连接状态为SYN_SEND(同步发送)。服务端若成功收到同步报文段,则创建一个连接状态为SYN_RCVD(同步接收)的半连接。此时双方都认为第一次握手完成。
  3. 第二次握手: 服务端向客户端发送同步和确认应答的报文(确认客户端发来的同步报文已收到),客户端若成功收到,代表第二次握手完成,此时客户端连接状态为ESTABLISHED,可以进行数据读写。
  4. 第三次握手: 客户端建立连接完成后,向服务器发送应答(确认服务器发来的同步报文已收到),服务端若成功收到应答,代表三次握手完成,则创建一个状态为ESTABLISHED的全连接,可以进行数据读写。

🔎什么是全连接和半连接?

  • 连接状态为SYN_SEND或SYN_RCVD的为半连接,状态为ESTABLISHED的为全连接。服务端的TCP传输层内核维护着两个队列:半连接队列和全连接队列,这两个队列与服务端的listensocket相关联,全连接队列的长度= listen的第二个参数backlog + 1

  • 半连接队列中保存处于SYN_SEND或SYN_RCVD的连接请求,半连接长时间未变为全连接会被清除。

  • 连接队列(即全连接队列)用于存储尝试连接到该listensocket,但尚未被服务器应用层accept函数接受的客户端连接请求。服务端应用层调用accpet就是获取全连接队列中的连接并建立新的文件fd供用户读写数据。

  • 如果全连接队列满了,多余的连接则无法进入ESTABLISHED状态(始终处于同步接收状态),即无法从半连接队列进入全连接队列,过段时间会被OS自动删除。

    设置backlog为1,全连接队列长度为2,此时只能存储两个连接,第三个连接到达服务端时,状态为SYN_RECV,无法进入全连接队列

    在这里插入图片描述

    过了一会,服务端清除SYN_RECV连接,客户端仍然维护着该连接

    在这里插入图片描述

📝三次握手的优越性

三次握手建立连接时,发送的报文也存在丢包问题。

如果第一次握手的SYN报文丢包,客户端会触发超时重传机制,即使超时重传也失败了,对双方也并没有任何影响,因为客户端和服务端都没有消耗维护连接的成本。如果第二次握手的SYN+ACK报文丢包,同上,也不会对双方超时影响。

问题在于第三次握手。第三次握手是客户端已经建立好连接,向服务器发送应答,此时客户端认为三次握手已完成,连接建立成功。如果第三次握手的应答丢包了,服务器没有收到应答,不会创建ESTABLISHED连接。此时客户端和服务器对于连接的状态认知不一致,连接失败。

  1. 三次握手保证了前两次握手都有应答来确认报文发送情况,可无论如何都会有最新一次应答无法确认,因此第三次握手的ACK应答无法确认是否发到服务器上。TCP协议采用的策略是不对第三次握手的ACK应答进行确认。客户端建立ESTABLISHED连接并将应答发出后,默认三次握手已完成,不管服务器是否收到应答,它便直接通过已建立的连接向服务器发送报文了。服务器收到客户端的报文后,如果发现本地没有与报文首部端口号相对应的连接,则会向客户端发送连接重置报文(RST标志位置1的报头)。客户端收到RST报文,重新触发三次握手,建立连接。 因此三次握手的本质是在“赌”最后一次握手是否成功,成功是大概率事件,即使失败也能后续重连。

  2. 根据上一条目所述,若三次握手的最后一次握手发送的应答丢包,此时客户端已维护了一个连接,而服务器没有,此时连接失败的成本就在客户端,无效的连接会占用客户端内存资源。在CS模式(Client and Server)中,客户端是主动发起连接请求的一方,而服务器端是等待接收连接请求的一方。因为服务器一般会同时维护多个连接,处理来自不同客户端的数据,如果连接失败的成本让服务端承担,会导致服务器性能下降,浪费服务器资源,所以连接失败的成本最好嫁接到客户端上。而三次握手恰能满足这种需求。如果是两次握手呢?见如下分析:

在这里插入图片描述

🤝🏼两次握手:如果第一次握手丢包,没问题,双方都没建立连接对象,消耗成本。由于第二次握手即最后一次,服务端发出SYN+ACK后就认为两次握手结束,便建立了连接对象,如果SYN+ACK丢包,没有到达目标客户端,则连接失败,此时客户端并没有建立连接,连接失败的成本在服务端上,并不符号我们的预期。两次握手还有另外的问题,客户端任意发送SYN请求,服务器收到后没有其它确认机制,直接在本地创建连接,而客户端却可以不对服务端的应答进行处理,也就不会消耗成本。这样一来,服务端极易收到大量客户端的SYN请求,造成严重的空间泄漏,这种攻击称为SYN洪水。真正预防攻击的机制应该由应用层完成,但是传输层TCP也绝不能有这种明显的漏洞出现。综上得,两次握手的方案不可取。

综合三次握手和两次握手的过程,得出结论:最后一次应答报文由哪一方发出,连接失败的成本就由哪一方承担。 又因为客户端是主动发起连接请求的一方,服务器端是等待接收连接请求的一方,由此推出:奇数次握手——失败成本在客户端,偶数次握手——失败成本在服务端。 因此必须采取奇数次握手的策略!

  1. 既然要采取奇数次握手的策略,为何TCP是三次握手,而不是五次、七次握手呢?事实上,握手的功能除了建立连接,还有验证双方通信信道的通畅情况。因为TCP传输是全双工的,因此想要验证全双工信道通畅情况,三次握手是最小成本,且有助于建立连接尽快完成。

2️⃣四次挥手断开连接

TCP连接通信结束后,要经过四次挥手断开连接。

通信双方,任一方都可以调用close主动发起断开连接请求,触发四次挥手,下面以客户端向服务器主动发起断连请求为例。

在这里插入图片描述

断开连接必须双方达成共识后才能断,即客户端调用一次close,对应服务端也应调用一次close。不同于三次握手,因为服务端收到断连请求FIN后,必须由应用层调用close后,才能再向客户端发送FIN请求,因此不能像三次握手一样发送ACK+SYN,而是先发送确认应答后,等待应用层close后才发送FIN请求。因此挥手规定是四次,两次挥手无法保证让双方达成共识,三次挥手更无法保证,因此四次挥手是断开连接的最小成本。

四次挥手的状态变化

  1. 客户端close(fd),连接立即进入FIN_WAIT_1状态,并向服务端发送FIN请求,即告知服务端“我”已经关闭连接了,等待应答。
  2. 服务端收到FIN请求后,连接立即进入CLOSE_WAIT状态,并向客户端发送ACK应答。此时服务端并没有关闭连接,而是保持CLOSE_WAIT状态,等待应用层close。应用层可以通过read的返回值0,得知客户端已关闭连接,进而调用close关闭连接。
  3. 客户端若成功收到ACK应答,连接则进入FIN_WAIT_2状态,等待服务端发送FIN请求(“我”已经告知对端我要断连了,当然也要获知对方也要断了,以达成共识)。若服务端一直没有发送FIN请求,客户端也不会在FIN_WAIT_2状态一直等下去,到达一定的时间,直接断开连接,清除连接数据。
  4. 服务端在连接状态为CLOSE_WAIT时调用close,则会进入LAST_ACK状态,并向客户端发送FIN请求。
  5. 客户端若成功收到服务端的FIN请求,则会由FIN_WAIT_2状态进入TIME_WAIT状态,并向服务端发送最后一次ACK应答。TIME_WAIT会等待一段时间,结束后关闭连接。
  6. 服务端若在LAST_ACK状态成功收到,则关闭连接。至此四次挥手完成。

💭四次挥手过程中,客户端会等待一次应答,服务端也会等待一次应答。这里都存在超时重传机制,若累积到一定重传次数,认为连接异常,强制关闭连接,这样也关闭了连接。因此,如果服务端在客户端FIN_WAIT_2状态等待时间结束自动关闭连接之后再调用close,就会进入LAST_ACK状态并不断重发,因为客户端已经关闭连接,重发多少次都是无效的,因此达到一定上限后服务端就异常断连了。

这种机制提醒程序员们在进行网络服务器开发时,切记要在通信结束后close(fd)关闭连接,否则连接的客户端一多,服务器中就会残留非常多CLOSE_WAIT状态的连接,这种连接会维持很长一段时间,严重占用了服务器内存资源。这种情况称为文件描述符泄漏。

关于TIME_WAIT

  • 为什么要有TIME_WAIT状态?

    主动发出断连请求的一方会进入TIME_WAIT状态,主要目的是尽量保证对端能够收到最后一个ACK应答,以完成正常的关闭连接过程。因为如果发出ACK后就直接关闭连接,而不进入TIME_WAIT状态,可能ACK还没到达对端,连接异常无法发送过去,即使对端想要重发也无济于事,因为“我”已经关闭连接了。另一个目的是保证历史数据在通信信道中消失,防止影响下一次通信。要知道,通信双方进入四次挥手阶段,可能还有一些尚未被接收或迟到的数据残留在信道中,如果不让它们消失,可能下次建立同客户端同服务端的连接时,就会收到历史残留的数据,产生意料之外的影响。

  • TIME_WAIT要等多久?

    TCP规定TIME_WAIT的时间是两个MSL(Maximum Segment Lifetime, TCP报文在网络中的最大存活时间),以保证全双工两个信道上的历史数据全部消失。MSL的值通常是2分钟(120秒),但在不同的TCP实现中,它也可以有所不同。

    Centos7默认配置为60s

在这里插入图片描述

  • TIME_WAIT导致服务器无法立刻重启问题

    若服务器端主动与客户端断开连接(这种场景也是存在的,比如服务器满载崩溃),必定会进入TIME_WAIT状态,此时再用相同的端口号重启服务器会bind失败,因为本地还有该端口号的连接处于TIME_WAIT状态,bind无法绑定已被使用的端口号。一般服务器的端口号是不能随意改变的,所以要想以相同端口号重启服务器,必须等待TIME_WAIT状态结束。但这是不可取的,TIME_WAIT的等待时间较长,一般大型服务器挂个几秒钟就会亏损很多,更何况是一两分钟。

    解决方案:

    setsockopt函数可以改变套接字状态,使其可以重复绑定已被使用的端口号

    // 函数原型:
    	int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
    // 用法:
            int optval = 1;
            setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    

在这里插入图片描述

⭕完整流程图:

在这里插入图片描述


4. 提高性能

💭TCP协议为了实现可靠性,设计十分复杂,这势必会导致一些通信场景下的效率降低,因此,TCP在可靠性的基础上,也采用了一些提高性能的机制。

流量控制

💭发送端的发送速度必须与接收端的接收能力相匹配,否则会导致效率下降。

  • 若发送速度过快,接收端接收能力不足。这里的“接收能力”指的是接收端接收缓冲区的剩余容量,比如发送端一次发送了1024个字节,而此时接收端接收缓冲区剩余空间仅有512字节,且应用层迟迟不read数据,那么接收端就没能力接收数据,溢出的512字节会被丢弃。根据确认应答机制,发送端还会重传一部分数据,影响了通信效率。
  • 若发送速度过慢,接收端长时间空闲。好比现在接收端缓冲区还有1024字节的剩余空间,而发送端每次只发送1字节数据,接收端的应用层一次read的数据非常少,处理数据的效率也大大降低。

流量控制是TCP协议中用于确保在数据传输期间发送方发送速度与接收方接收处理能力相匹配的一种机制。

TCP报头中的窗口大小字段是流量控制机制的核心。 接收端每次向发送端发出一个报文,都会携带其目前接收缓冲区的剩余空间大小,即窗口大小。发送端根据接收端的窗口大小,控制发送数据的大小,确保发送速度与接收端接收能力匹配,这就是流量控制机制。


滑动窗口

前面我们讨论的情况,都是假设通信是串行的,即TCP发送端每次只发送一个报文,然后再根据接收端应答的确认序列号,发送下一个报文。这么做效率太低,实际TCP中每次是同时发送多个报文,一个报文的发送不用等待上一个报文的确认应答,并发发送,时间重叠,提高效率。

在这里插入图片描述

  • TCP中的滑动窗口实际上是发送缓冲区中的一段区间,这段区间中的数据是可以不用等待应答直接发送的数据。

  • 滑动窗口控制每次发送的数据范围,即每次发送应用层数据都是发送滑动窗口中的数据。

  • TCP初始滑动窗口大小一般在三次握手建立连接期间被确定,滑动窗口的大小变化根据应答报文中的窗口大小决定。(拥塞窗口也是决定因素之一,后面讲)

滑动窗口在发送缓冲区中的结构:

在这里插入图片描述

  1. 滑动窗口之前的数据是已发送已确认应答的数据,这部分数据已在发送缓冲区中无效,可以被覆盖。

  2. 滑动窗口中,前段部分是已发送但未确认应答的数据,根据确认应答-超时重传机制,这部分数据必须保留一段时间,以支持重传,后段部分是可以直接发送,不用等待上个报文确认应答的数据。每次收到接收端的应答,发送端都会根据该应答更新滑动窗口的大小。应答的确认序列号决定滑动窗口前半部分哪些数据已被确认,应答的窗口大小影响滑动窗口这次能够发送多大数据。

  3. 滑动窗口之后的数据是尚不能发送的数据,应用层wirte/send写入数据尾插到这一部分,等待滑动窗口到达。

滑动窗口工作过程:

  1. 初始时,假设发送端滑动窗口大小为5,此时窗口内的5个报文可以同时发送,但这5个报文的应答不一定同时到达发送端。

  2. 发送端收到最新一个ACK应答后,会根据该应答调整滑动窗口的大小。假设滑动窗口的区间为[winBegin, winEnd] :

    winBegin = 确认序列号

    winEnd = winBedin + 窗口大小

  3. 发送端将已更新的滑动窗口中的数据(包含前半部分的重传数据和后半部分首次传输的数据)全部发出。

💡总而言之,滑动窗口的工作过程就是不断向右滑动(不能向左,因为左边的数据已发送已应答),发送窗口中的数据。值得注意的是,由于TCP发送缓冲区设置为环状结构,因此滑动窗口不会越界

🔎异常情况:

  1. 若多个同时发出的报文,部分确认应答丢包了。不要紧,因为确认序列号X是接收端告知发送端"X之前的数据已经收到了,下次从X开始发送",所以后续的应答也能够确认前面的报文。即使应答都丢了,也还有重传机制兜底。

在这里插入图片描述

  1. 若是发出的数据报文丢包了

    在这里插入图片描述

    如图,如果是第一个报文丢包了,其它报文没丢,接收端会收到2001~5000的三个报文,并且后续每个报文的应答确认序列号都是1001(因为1001~2000的报文没收到,要告知发送端下次从1001开始发)。随后发送端会收到三个重复的确认序列号。

    原本报文丢包是超时重传机制解决的,第一个报文没收到对应的确认应答会等待超时重传。而发送端在等待期间却收到了三个重复的确认序列号1001,根据重复的确认序列号1001,锁定最近的报文,判断1001~2000已丢包,不再等待超时重传,直接补发1001~2000的数据。接收端成功收到空缺的数据后,下次就发送确认序列号5001了。 这种机制称为“高速重传机制”(又称快重传)。

  • 快重传与超时重传的区别

    快重传规定发送端收到三个或三个以上(两个可能是其它因素导致的重复ACK,不可靠)的重复确认应答,立即补发以快速恢复丢失报文,不等待超时。 超时重传是重传机制的下限,快重传是重传机制的上限,是在超时重传的基础上做效率优化。


拥塞控制

实际上,在网络世界中,通信双方进行数据传输时的效率,不仅会受到彼此的影响,还会受到网络的影响。网络中存在大量的主机,可能当前的网络情况比较拥堵,如果通信双方在不清楚当前网络状态下,贸然向网络中发送大量数据,可能会引发大量的丢包。而丢包又会触发重传,重传又再次将大量数据发入网络,又加剧了网络的拥堵情况,这是一种恶性循环。

🚄为了保证传输的可靠性,提高传输效率,通信双方必须清楚网络状态,根据网络情况决定数据吞吐量,TCP设计了拥塞控制机制。拥塞控制机制一般由慢启动和拥塞避免两部分构成。

  1. 慢启动

    通信刚开始时,双方并不清楚网络状态,只能先以较慢速度传输数据,以摸清网络状态。若传输数据正常收到应答,则加快传输速度(这里的传输速度指每次传输的数据量大小),直到出现丢包问题,这就是慢启动。慢启动的加快呈指数增长,指数增长的特点是先慢后快,符合刚开始不能向网络发送大量数据,又想尽快摸清网络状况的需求。

  2. 拥塞避免

    为了不增长的过快,慢启动达到某个阈值后会由指数增长变为线性增长,此后的过程称为拥塞避免。

💬拥塞窗口:

为了量化网络重载通信传输的能力,TCP中维护了一个拥塞窗口,慢启动到拥塞避免的过程称为拥塞探测,拥塞探测的本质就是在不断更新拥塞窗口的大小,因为网络状况是不断变化的,所以拥塞探测在通信过程中是持续进行的。

拥塞窗口是更新滑动窗口大小的另一指标,发送端每次更新滑动窗口时:滑动窗口大小 = min(接收端窗口大小,拥塞窗口大小)

⭕拥塞探测的状态变化:

在这里插入图片描述

刚开始先是慢启动,到达ssthresh阈值后(TCP一般设置初始ssthresh为65535字节)进入拥塞避免,即线性增长,每次传输拥塞窗口加1。探测时发生大量丢包则认为是网络拥塞,此时将拥塞窗口reset为1,以降低网络压力,并重新开始拥塞探测(先让网络缓一缓,再慢慢试探它)。

  • 每次新的探测的ssthresh阈值是上一次的一半。

  • 网络拥塞在探测全程都有可能出现,不一定是在拥塞避免阶段。


延迟应答

💭通信双方发送数据,希望每次发送的数据量尽量大一些,这样可以减少发送次数,因为过多的网络发送次数会降低效率。发送端发送的数据量,取决于网络情况(拥塞窗口cwnd)和对端接受能力(接收窗口大小rwnd),网络情况不是通信双方能决定的,因此TCP的策略是尽量加大每次发送端收到的rwnd。rwnd由接收端交付给发送端,因此rwnd需在接收端计算。rwnd是接收缓冲区的窗口大小,这不是TCP能随意扩大的,而是要等TCP上层的应用程序read/recv读取缓冲区中的数据后,rwnd才会变大。因此,TCP能做的只有延缓应答的时间,加大应用层读取数据的概率,尽可能让rwnd更大再交付给发送端,这就是延迟应答。

延迟应答除了延时应答,还有隔包应答:

  1. 延时应答:超过一定时间再返回应答
  2. 隔包应答:每个N个包就应答一次

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


捎带应答

💭在确认应答机制里,TCP报头中的起作用的只有ACK标志位和确认序列号。因此,为了减少网络发送次数,应答不一定独立成为一个报文,而是搭乘其它数据报文的“顺风车”,仅需将该报文中的ACK标志位置为1,填上正确的确认序列号即可。

在这里插入图片描述


5. 面向字节流

🌊TCP协议是面向字节流的,发送端应用层调用write/send是先向TCP的发送缓冲区写入应用层报文数据,数据怎么发,发多少由TCP决定。

若TCP收到的数据过长,则会拆分成多个TCP报文,若收到的数据过短,则会等待一段时间再打包一个TCP报文。

接收端应用层调用read/recv,并无法保证一次就能提取一个完整的应用层报文,因为TCP是面向字节流的,TCP数据发送到接收端时是多个TCP报文,这些TCP报文可能一个里面包含多个应用层报文,也可能多个TCP报文才能组成一个应用层报文。TCP会根据报文序号自动拼接,组成一段顺序与发送方数据一致的数据,再交付给应用层。

应用层调用read/recv看到的永远都是一段字节流数据。由于缓冲区的存在,TCP程序的读和写不需要一一匹配,例如:写100个字节数据时,可以调用一次write写100个字节,也可以调用100次write,每次写一个字节;读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次read 100个字节,也可以一次read一个字节,重复100次。

  • 粘包问题

    因为应用层调用read/recv看到是字节流数据,无法直接区分报文与报文之间的间隔,因此需要应用层自己定制协议规定如何提取报文,明确两个应用层报文之间的边界。例如HTTP协议中采用的是Content-Length加空行的策略提取报文。


6. TCP/UDP对比

💭TCP/UDP对比

我们说了TCP是可靠连接,那么是不是TCP一定就优于UDP呢?TCP和UDP之间的优点和缺点,绝对不能简单地进行比较

  • TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景;
  • UDP用于对高速传输和实时性要求较高的通信领域,例如:早期的QQ,视频传输等。另外UDP还可以用于广播。

归根结底,TCP和UDP都是程序员的工具,什么时机用,具体怎么用,还是要根据具体的需求场景去判定。


ENDING…

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

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

相关文章

threejs(7)-精通粒子特效

一、初识Points与点材质 // 设置点材质 const pointsMaterial new THREE.PointsMaterial(); import * as THREE from "three"; // 导入轨道控制器 import { OrbitControls } from "three/examples/jsm/controls/OrbitControls"; // 导入动画库 import gsa…

854算法之线性表

周小伦说的建议王道的所有算法题最好都写一下啊,尤其是树的,排序相关的要写一下,然后还有链表,链表有一些反转链表啊一些经典的代码肯定要背的呀,比如说,三种遍历的递归和非递归,怎么找树的宽度…

【C程序设计】用心浇灌<C程序>

目录 数据类型 整数类型 实例 浮点类型 void 类型 类型转换 数据类型 在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。 C 中的类型可分为以下几种&…

服务端推送、 server sent event、sse、springboot+sse

SSE(server-sent events) SSE全称server-sent events,翻译过来是服务端发送事件,通常的http请求,客户端请求,服务端返回响应,一次只能返回一个值;SSE使用的http协议,客户端请求后,服…

bbr 的 “最优操作点”

最近做一组测试,我复现了一组结果准备阐释另一个事。先看这个测试结果: 常规的一个 wrk2(expected_latency_timing 改为 actual_latency_timing 计数) 压 nginx 的测试,调整 -R 参数,Req/sec 同步增加,当 Req/sec 不…

(免费领源码) Asp.Net#SQL Server校园在线投票系统10557-计算机毕业设计项目选题推荐

摘 要 随着互联网大趋势的到来,社会的方方面面,各行各业都在考虑利用互联网作为媒介将自己的信息更及时有效地推广出去,而其中最好的方式就是建立网络管理系统,并对其进行信息管理。由于现在网络的发达,校园投票通过网…

液氮恒温器主要特点

恒温器是直接或间接控制一个或多个热源和冷源来维持所要求的温度的一种装置。 主要特点 更宽温区:液氮恒温器的温区宽度扩展到了80K~500K,为液氮温区实验用户提供更宽温区解决方案。 操作更简单:液氮恒温器在样品更换、液氮填充、控制液氮…

UI自动化概念 + Web自动化测试框架介绍

1.UI自动化测试概念:我们先明确什么是UI UI,即(User Interface简称UI用户界面)是系统和用户之间进行交互和信息交换的媒介 UI自动化测试: Web自动化测试和移动自动化测试都属于UI自动化测试,UI自动化测试就是借助自动化工具对程序UI层进行自动化的测试 …

【分享】RAR压缩包的密码可以取消吗?

在日常工作中,我们可能经常用到RAR文件,RAR作为一种常见的压缩文件格式,可以将一个或多个文件压缩成单个文件,方便传输和保存。 RAR文件还可以设置“打开密码”,这样只有输入正确的密码才能打开压缩包里面的文件&…

SpringBoot 快速实现 api 加密

在项目中,为了保证数据的安全,我们常常会对传递的数据进行加密。常用的加密算法包括对称加密(AES)和非对称加密(RSA),博主选取码云上最简单的API加密项目进行下面的讲解。 下面请出我们的最亮的…

5 个编写高效 Makefile 文件的最佳实践

在软件开发过程中,Makefile是一个非常重要的工具,它可以帮助我们自动化构建、编译、测试和部署。然而,编写高效的Makefile文件并不是一件容易的事情。在本文中,我们将讨论如何编写高效的Makefile文件,以提高我们的开发…

【Bug——VMware Workstation】虚拟机桥接网络没有 VMnet0

此时 没有VMnet0用来桥接网络。 接下来进行解决 1.找到安装VM的路径,在安装的目录里面找到如图所示的三个文件: 2.依次点击鼠标右键 将这三个文件依次安装如图所示: 二.windows下的操作 1.首先 找到电脑的控制面板->网络和internet->…

二维码智慧门牌管理系统升级解决方案:采集计划精细化管理的艺术

文章目录 前言一、采集计划的定义和配置流程二、多采集计划配置策略三、采集计划的实践应用 前言 在数字化时代,建设智慧城市需要借助各种先进的技术工具。其中,二维码智慧门牌管理系统在城市管理、资源调配和公众服务等方面扮演着举足轻重的角色。关键…

不容错过的Spring框架解析,掌握开发中的关键技术

Spring 原理 它是一个全面的、企业应用开发一站式的解决方案,贯穿表现层、业务层、持久层。但是 Spring仍然可以和其他的框架无缝整合。 Spring 特点 轻量级 控制反转 面向切面 容器 框架集合 Spring特征 Spring 核心组件 Spring 常用模块 Spring 主要包 Spring …

Self-Supervised MultiModal Versatile Networks

方法 视觉-音频空间S v a _{va} va​,嵌入z v , v a t _{v,vat} v,vat​ 体会 一篇文章只有一张图,笑死人。作者且未回复问题

体育竞技分析

体育竞技分析是一项重要的工作,它通过对运动员的数据进行收集、处理和分析,可以帮助教练和球队做出更明智的决策。Python作为一种强大的编程语言,提供了丰富的库和工具,可以用于体育竞技数据的分析和可视化。本文将介绍如何使用Py…

二、BurpSuite Decoder解码器

一、编码解码 解释:BurpSuite 可以用这个模块来轻松进行编码解码,下面是支持的类型 URL HTML Base64 ASCIIhex Hex Octal Binary Gzip 注意:特别注意的是URL编码,一般的在线网站都无法对比如‘abc’的文本编码,burps…

【0基础学Java第二课】数据类型与变量

2. 数据类型与变量 2.1 字面常量2.2 数据类型2.3 变量2.3.1 变量概念2.3.2 语法格式 2.4 整型变量2.4.1 整型变量2.4.2 长整型变量2.4.3 短整型变量2.4.4 字节型变量 2.5 浮点型变量2.6 字符型2.7 布尔型变量2.8 类型转换2.9 类型提升2.10 字符串类型2.10.1 字符串拼接操作符 2…

linux中nginx配置https

一、版本适配 版本一定要适配,否则会报错各种参数定位不到不识别的错误,以下是版本适配信息,各位观客自行按照以下信息匹配版本。 Nginx 1.11.5及以上版本与OpenSSL 1.0.2及以上版本兼容。Nginx 1.15.2及以上版本与OpenSSL 1.1.1及以上版本兼…

足底筋膜炎多久能好

足底筋膜炎4-6周左右能好。 足底筋膜炎一般经过系统治疗后,如外敷‘古顺敷堂’筋膜贴在3-5天的时间水肿明显的减轻,度过水肿期之后,疼痛的症状能够得到明显的缓解,但是如果进行下床活动以及剧烈的运动、受凉,还可能会…