【Linux】【网络】传输层协议:TCP

文章目录

  • TCP 协议
      • 1. TCP 协议段格式
      • 2. TCP 报头解析
      • 3. TCP 的可靠性
      • 4. 面向字节流
      • 5. 粘包问题
      • 6. 连接队列维护
  • TCP 的 确认应答机制
  • TCP 的 超时重传机制
  • TCP 的 三次握手
  • TCP 的 四次挥手
      • setsockopt 函数:设置套接字选项,解决 TIME_WAIT 状态引起的 bind 失败
  • TCP 的 流量控制
  • TCP 的 滑动窗口
  • TCP 的 拥塞控制:慢启动机制 和 阈值
  • TCP 的 延迟应答
  • TCP 的 捎带应答
  • TCP 的 异常情况
  • 小结

TCP 协议

TCP(Transmission Control Protocol 传输控制协议)

  • 传输层协议。
  • 有连接:处于通信之前,也就意味着三次握手是不携带有效信息的。
  • 可靠传输:有确认机制如 收到应答、超时重传、三次握手、四次挥手…
  • 面向字节流:有对应的以字节为单位的缓冲区收发数据以供解析。

对比 UDP:

UDP(User Datagram Protocol 用户数据报协议)传输的过程类似于寄信。
- 传输层协议。
- 无连接:知道对端的IP和端口号就直接进行传输,不需要建立连接。
- 不可靠:没有确认机制,没有重传机制;如果因为网络故障该段无法发到对方,UDP协议层也不会给应用层返回任何错误信息。
- 面向数据报: 不能够灵活的控制读写数据的次数和数量。

1. TCP 协议段格式

TCP 的首部有一块定长大小为 20 字节的空间字段,用于报头信息的填写。后面接着的是一块储存选项的空间,也可能没有选项信息。

既然不确定选项字段的情况,如何分离报头和有效载荷呢?

  • 在 TCP 前 20 字节的定长字段之中有一个记录首部长度的空间,这部分空间大小是 4 个比特位,而其中记录的单位大小是 4 字节,即可以记录 60 字节的长度,前 20 是定长的,后 40 就是给头部选项的。

  • 所以整个首部的大小 = 首部长度字段 * 4 - 20,如果 == 0,则报头读取完毕,剩下的就是有效载荷
    在这里插入图片描述


2. TCP 报头解析

源 / 目的端口号

  • 表示数据是从哪个进程来,到哪个进程去

32 位序号 / 32 位确认号

  • 对报文的编号,和确认报文的编号(见 TCP 的可靠性)

4 位 TCP 报头长度

  • 表示该 TCP 头部有多少个 32 位 bit(有多少个 4 字节); 所以TCP头部最大长度是15 * 4 = 60

6 个重要标志位

  • ACK - - acknowledge:该报文是一个确认报文,确认报文可能会携带数据,携带数据的确认报文的应答叫做 捎带应答。

  • SYN – sync:请求建立连接,我们把携带 SYN 标识的称为 同步报文段

  • FIN:请求断开连接,通知对方,本端要关闭了,我们称携带 FIN 标识的为 结束报文段

  • RST - - reset:对方要求重新建立连接,我们把携带 RST 标识的称为复位报文段。

  • PSH - - push:提示接收端应用程序立刻从 TCP 缓冲区把数据读走。

  • URG - - urgent:标识紧急指针字段是否有效。

16 位窗口大小

  • 发送自己端接收缓冲区的剩余空间大小,以作 流量控制,不会导致对方发太快太多导致丢包,也可以避免发的太慢效率低下的问题出现。

16 位校验和

  • 发送端填充,CRC 校验。接收端校验不通过,则认为数据有问题,直接丢弃。此处的检验和不光包含 TCP 首部,也包含 TCP 数据部分。

16 位紧急指针

  • 是一个偏移量,标识哪部分数据是紧急数据(一个字节的状态码,这样的数据也叫做外带数据)。

40 字节头部选项:见后文。

其中 紧急数据 的处理,可以在 recv 和 send 中设置:

ssize_t recv(int sockfd, void *buf, size_t len, int flags);ssize_t send(int sockfd, const void *buf, size_t len, int flags);

参数 flags:

  • MSG_OOB:允许 接收 / 发送 非正常流中的 紧急数据
但在实际使用中 URG 使用的很少,因为他能携带的信息量实在有限,
更多的是多开一个端口号,拿到并处理特殊信息

3. TCP 的可靠性

不可靠的情况有很多,比如:丢包、乱序、重复、校验失败、发送太快/太慢、网络问题…

客户端发送请求,在收到服务器响应,才可以 100% 的确保对方是收到的,也就是说 可靠性,是通过收到应答机制保证的。如此也说明,我们无法保证任何报文都是可靠送达,但可以局部保持可靠性。

实际上客户端可以同时对服务器发送多个请求报文,经过网络的传输,到达服务器的情况却不一样,应对不同的报文送达情况,客户端有不同的应对机制,判断哪个报文是哪个报文就是很有必要的。所以报文里会携带两种编号:序号、确认序号。确认序号是收到序号 +1 来设定的。两个序号在一段报文中注定是更高效的,所以在报头中有各自不同的字段空间。

序号的设置保证了 TCP 的可靠性。

4. 面向字节流

创建一个 TCP 的 socket,相当于同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区

  • 调用 write 时,数据会先写入发送缓冲区中。

  • 如果发送的字节数太长,会被拆分成多个 TCP 的数据包发出;如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去。

  • 接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区。

  • 然后应用程序可以调用 read 从接收缓冲区拿数据。

  • 另一方面,TCP 的一个连接,既有发送缓冲区,也有接收缓冲区,那么对于这一个连接,既可以读数据,也可以写数据。这个概念叫做 全双工

由于缓冲区的存在,TCP 程序的读和写不需要一一匹配,例如:

写 100 个字节数据时,可以调用一次 write 写 100 个字节,也可以调用 100 次 write,每次写一个字节。
读 100 个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次 read 100 个字节,也可以一次 read 一个字节,重复100次。

5. 粘包问题

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

在 TCP 的协议头中,没有如同 UDP 一样的 “报文长度” 这样的字段,但是有一个序号这样的字段。

  • 站在传输层的角度,TCP 是一个一个报文过来的。按照序号排好序放在缓冲区中。
  • 站在应用层的角度,看到的只是一串连续的字节数据。那么应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分,是一个完整的应用层数据包。

避免粘包问题, 归根结底就是, 明确两个包之间的边界

  • 对于定长的包,保证每次都按固定大小读取即可。例如上面的 Request 结构,是固定大小的,那么就从缓冲区从头开始按 sizeof(Request) 依次读取即可。

  • 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置。

  • 对于变长的包,还可以在包和包之间使用明确的分隔符(应用层协议, 是程序员自己来定的, 只要保证分隔符不和正文冲突即可)

对于 UDP 协议来说,是否也存在 “粘包问题” 呢?

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

6. 连接队列维护

Linux 内核协议栈为一个 TCP 连接管理使用两个队列。

  1. 半链接队列(用来保存处于 SYN_SENT 和 SYN_RECV 状态的请求)
  2. 全连接队列(accepted 队列)(用来保存处于established状态,但是应用层没有调用accept取走的请求)

全连接队列:管理已连接但还没发起任务的客户端。为了保证服务器的使用率,全连接队列的维护是必须维护的。也要控制这个队列不能太长。

  • 因为可能会过多消耗 OS 本来是给 server 使用的资源,且队列太长 client 会不愿意等。
  • 实际上更有效率的应该是 提高 网络吞吐量。
  • 至于队列究竟要维护多长,需要看各自应用场景。

半连接队列:管理半连接状态的队列。

TCP 协议,需要在底层维护 全连接队列,最大长度是:listen() 的第二个参数 +1


TCP 的 确认应答机制

在这里插入图片描述

在确认应答机制中,TCP 首先将每个发出的数据,在发送缓冲区中都以字节为单位进行了编号,即序列号。每一个 ACK 都带有对应的确认序列号(序列号 +1),意思是告诉发送者,我已经收到了哪些数据,下一次你从哪里开始发。

例如,主机 A 和主机 B 通信:
A to B:发送数据(1~1000)
B to A:确认应答(收到了,下一个从 1001 开始发)
A to B:发送数据(1001~2000)
B to A:确认应答(收到了,下一个从 2001 开始发)
...

在字节单位的缓冲区中不断的读取和放置,就是 TCP 的特征之一,面向字节流。


TCP 的 超时重传机制

主机 A 发送数据给 B 之后,可能因为网络拥堵等原因,数据无法到达主机B。如果主机 A 在一个特定时间间隔内没有收到 B 发来的确认应答,就会进行重发。

例如,主机 A 和主机 B 通信:
A to B:发送数据(1~1000)
// 一段时间没有收到 B 的确认应答,A 判定丢包,开始重传
A to B:发送数据(1~1000)
B to A:确认应答(收到了,下一个从 1001 开始发)

因此主机 B 会收到很多重复数据,那么 TCP 协议需要能够识别出那些包是重复的包,并且把重复的丢弃掉。这时候报头里的序列号,就可以很容易实现去重。

超时的时间如何确定呢?

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

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

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

TCP 的 三次握手

整个 TCP 三次握手建立连接,传递信息,四次挥手断开连接的过程,包括应用层的建立。我们需要关注的是传输和两端的状态变化,流程图如下:
在这里插入图片描述

三次握手的过程,由双方的 OS 系统中的 TCP 层自主完成。

客户端:connect,触发连接,等待完成
服务器:accept,等待建立完成,获取连接

为什么握手是三次?

  • 没有明显的设计漏洞(如果是 2 次会造成客户端零成本连接,服务器可能收到连接攻击而崩溃),一旦建立连接出现异常,成本嫁接到 client 端,server 端成本较低(因为最后一次发送可能丢失,如果偶数次握手,server 是不确定丢包与否即建立成功与否的)。

  • 可以验证双方通信信道的通畅情况,三次握手是验证全双工通信信道通畅的最小成本。

三次握手除了确认信道畅通建立连接,还有什么用?

  • 通过 16 位窗口大小进行流量控制。首先我们要知道,TCP 是面向连接的,处于正常通信之前,三次握手是不携带有效数据的,也就是说,两端第一次传输数据不是第一次通信。
  • 在三次握手阶段,首先,客户端在发起连接请求时,除了 SYN 标志位被置 1,客户端也一定会将自己的 16 位窗口大小通告给服务器,以便服务器知道该客户端的承载能力。
  • 其次,服务器响应 SYN + ACK 标志位被置 1,服务器的 16 位窗口大小也会被填上。这样双方的承载能力就都被对方知晓了,不会在第一次报文传输的时候出现流量异常导致的一系列问题。

服务器端状态转化:

  • [CLOSED -> LISTEN] 服务器端调用 listen 后进入 LISTEN 状态,等待客户端连接

  • [LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送 SYN 确认报文

  • [SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文,就进入 ESTABLISHED 状态,可以进行读写数据了

  • [ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用 close),服务器会收到结束报文段,服务器返回确认报文段并进入 CLOSE_WAIT

  • [CLOSE_WAIT -> LAST_ACK] 进入 CLOSE_WAIT 后说明服务器准备关闭连接(需要处理完之前的数据),当服务器真正调用 close 关闭连接时,会向客户端发送 FIN,此时服务器进入 LAST_ACK 状态,等待最后一个 ACK 到来(这个ACK是客户端确认收到了 FIN)

  • [LAST_ACK -> CLOSED] 服务器收到了对 FIN 的 ACK,彻底关闭连接

客户端状态转化:

  • [CLOSED -> SYN_SENT] 客户端调用 connect,发送同步报文段

  • [SYN_SENT -> ESTABLISHED] connect 调用成功,则进入 ESTABLISHED 状态,开始读写数据

  • [ESTABLISHED -> FIN_WAIT_1] 客户端主动调用 close 时,向服务器发送结束报文段,同时进入 FIN_WAIT_1

  • [FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,开始等待服务器的结束报文段

  • [FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段,进入 TIME_WAIT,并发出 LAST_ACK

  • [TIME_WAIT -> CLOSED] 客户端要等待一个 2 MSL(Max Segment Life,报文最大生存时间)的时间, 才会进入 CLOSED 状态。

TCP 的 四次挥手

综合上述的描述,对四次挥手进一步做些解释。

四次挥手对于主动断开的一方 进行最后一次确认后,要进入 TIME_WAIT 状态,这是一个临时性状态,持续一会就没了。

为什么挥手是四次?

  • 建立连接后,两端就是同等地位了,一方断开连接都需要另一方确认。

TIME_WAIT 状态的细节:

  • 当主动断开的一方进入 TIME_WAIT 状态时,连接确实就断开了,但底层这个连接还没有被彻底关掉,相应的端口号还在被 TIME_WAIT 状态占用,在被占用的这段时间里,这个端口号就是不能使用的。

  • 如果是 server 端发起断开连接并处于 TIME_WAIT 状态,而 server 端经不起等待或者更换端口时,就可以调用下面的接口,对套接字进行相应的选项设置,无视其TIME_WAIT 状态,重新使原端口可以被有效使用。

setsockopt 函数:设置套接字选项,解决 TIME_WAIT 状态引起的 bind 失败

 #include <sys/types.h>          /* See NOTES */#include <sys/socket.h>
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);  
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

参数 level:

  • 要设的属性在哪一层

参数 optname:

  • 要设的是哪一个属性

  • SO_REUSEADDR:无视网络中的 TIME_WAIT 状态。主动关闭连接的一方处于 TIME_WAIT 的时候,允许新的连接重新绑定与 TIME_WAIT 状态的连接有冲突的 IP + PORT(),并立即接收数据。

  • SO_REUSEPORT: SO_REUSEADDR 对完全相同的IP+PORT绑定(无论是具体的IP还是通配)仍然出现Address already in use的错误,使用SO_REUSEPORT选项可以避免此错误。

参数 optval:

  • 属性的值设成多少

参数 optlenl:

  • 属性长度是多少

·

TIME_WAIT 状态有什么用?

  • 当退出端退出时,或有正在传输的信息并未到达对端,TIME_WAIT 的存在,就是为了让已退出端尚未完成传输的信息消散,目的是不影响对端 ACK 的序号不被先前信息影响,保证下次同个端口号发送的数据能够被正常响应。

  • 另一方面,是为了保证退出端的最后一次 ACK 被对端收到。


TCP 的 流量控制

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

  • 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 “窗口大小” 字段,通过ACK端通知发送端。窗口大小字段越大, 说明网络的吞吐量越高。

  • 接收端一旦发现自己的缓冲区快满了,就会将窗口大小设置成一个更小的值通知给发送端。发送端接受到这个窗口之后,就会减慢自己的发送速度。

  • 如果接收端缓冲区满了,就会将窗口置为0,这时发送方不再发送数据,但是需要定期发送一个 窗口探测 数据段,使接收端把窗口大小告诉发送端。

  • 窗口探测:发送方定期发出一个 TCP 报头,接收方必须 对所有请求进行应答(TCP 的协议内容),于是返回应答中的 16位 窗口字段就可以让发送方知道,什么时候可以继续发送有效载荷。

在TCP首部中,有一个 16 位窗 口字段,用来存放窗口大小信息。

那么问题来了,16 位数字最大表示 65535, 那么 TCP 窗口最大就是 65535 字节么? 

实际上, TCP 首部 40 字节选项中还包含了一个 窗口扩大因子 M,实际 窗口大小是 窗口字段的值左移 M 位


TCP 的 滑动窗口

在这里插入图片描述
对应确认应答策略,对每一个发送的数据段,都要给一个ACK确认应答,收到ACK后再发送下一个数据段。如此串行工作,性能较差,尤其在数据往返的时间较长的时候更甚。

一发一收的方式性能较低,只要一次发送多条数据,就可以大大的提高性能(其实是将多个段的等待时间重叠在一起了)。

  • 窗口大小指的是无需等待确认应答而可以继续发送数据的最大值,上图的窗口大小就是4000个字节(四个段)。

  • 发送前四个段的时候,不需要等待任何 ACK,直接发送。

  • 收到第一个 ACK 后,滑动窗口向后移动,继续发送第五个段的数据,依次类推。

  • 操作系统内核为了维护这个滑动窗口,需要开辟 发送缓冲区 来记录当前还有哪些数据没有应答;只有确认应答过的数据,才能从缓冲区删掉。

  • 窗口越大, 则网络的吞吐率就越高。

一般情况

在这里插入图片描述

  1. 滑动窗口往左边部分,是已经发送已经确认过的无效数据,可以被覆盖

  2. 滑动窗口部分,暂时不用等待收到应答,可以直接发送。其大小和对方的接受能力有关,即应答报文中的窗口大小。

  3. 滑动窗口往右边部分,是尚未发送的数据区域。

如何理解滑动窗口?

在这里插入图片描述

  • 所谓面向字节流的 TCP,他的接收和发送缓冲区都可以理解成一个个 char 类型的 数组。

  • 而滑动窗口在这个数组中的“滑动”,实际是依赖两个指针(这里做 winstart,winend)划定范围,通过 ++ 运算符完成的。

一些规则

  1. “窗口” 只会向右 “滑动”,左边是已经确认过的报文

  2. 一直向右移的规则并不会造成越界,因为发送缓冲区的结构是环状的。

  3. 滑动窗口的大小是浮动的,不是固定大小,而其 变大、变小、变 0 是根据对方给本端响应报头中 窗口大小 来调节的。

     窗口变大通过 winend+=xxx 来完成窗口变小 winend-=xxx 来完成
    
  4. 滑动窗口大小的更新,更具体来说,和对方发送的序列号 seq 有关,在应答也是按序到达的前提下:

    根据应答的 seq,winstart = seq;
    根据应答的 win,winend = winstart + win;
    
  5. 滑动窗口内部的报文可以直接发送,多个报文传输,肯定会发生丢失的问题。

    如果第一个丢失了情况1:数据丢了,即使后面的收到了,winstart 也不会向后移动,只是等待发送方超时重传。情况2:应答丢失,可以通过后面传来的响应报文确定,之前的一定接收到了。
    如果中间或最后的丢失了随着 winstart 的右移都会转化成第一个丢失问题,按上述步骤处理。
    
  6. 数据要支持重传,就必须被保存起来,保存的位置就是滑动窗口


TCP 的 拥塞控制:慢启动机制 和 阈值

少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为 网络拥塞

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

当网络拥塞出现大量丢包的情况:发送方不能一直超时重传也不能完全停发。总体策略是,保证网络拥塞不能加重,再网络拥塞有起色的情况下,尽快恢复网络通信。

慢启动 机制,先发少量的数据探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。

此处引入一个概念程为 拥塞窗口

  • 发送开始的时候,定义拥塞窗口大小为 1

  • 每次收到一个 ACK 应答,拥塞窗口加 1

    只是这样的话,拥塞窗口的大小肯定是指数级上升的,可实际不止如此。
    
  • 每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口

    滑动窗口 = min(对端主机的接受能力 win,网络的拥塞窗口)
    winstart = seq;
    winend = min(seq_win, 拥塞窗口);
    

正常来说拥塞窗口增长速度,是指数级别的。“慢启动” 只是指初使时慢,但是增长速度非常快。为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍,需要 慢启动的 阈值 进行控制。

在这里插入图片描述

  • 当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长。
  • 当 TCP 开始启动的时候,慢启动阈值等于窗口最大值
  • 在每次超时重发的时候,慢启动阈值会变成原来的一半,同时拥塞窗口置回 1

拥塞控制,归根结底是 TCP 协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。


TCP 的 延迟应答

如果接收数据的主机立刻返回 ACK 应答,这时候返回的窗口可能比较小。

假设接收端缓冲区为 1M,一次收到了500K的数据,
如果立刻应答,返回的窗口就是500K。
但实际上可能处理端处理的速度很快,10ms 之内就把 500K 数据从缓冲区消费掉了在上述情况下,接收端处理还远没有达到自己的极限,即使窗口再放大一些,也能处理过来。
如果接收端稍微等一会再应答,比如等待 200ms 再应答,那么这个时候返回的窗口大小就是 1M。

在这里插入图片描述

窗口越大,网络吞吐量就越大,传输效率就越高。我们的目标是在保证网络不拥塞的情况下尽量提高传输效率。如何选择延迟应答的时机呢?

选择延迟应答的时机,也有不同的方案:

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

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


TCP 的 捎带应答

在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是 “一发一收” 的。
意味着客户端给服务器说了 “你好吗”,服务器也会给客户端回一个 “我很好”。那么这个时候 ACK 就可以和服务器回应的 “我很好” 一起回给客户端。

在这里插入图片描述


TCP 的 异常情况

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

机器掉电 / 网线断开:接收端认为连接还在,一旦接收端有写入操作,接收端发现连接已经不在了,就会进行 reset。即使没有写入操作,TCP自己也内置了一个保活定时器,会定期询问对方是否还在,如果对方不在,也会把连接释放。

另外,应用层的某些协议,也有一些这样的检测机制

例如 HTTP 长连接中,也会定期检测对方的状态,例如QQ,。
在 QQ 断线之后,也会定期尝试重新连接。

小结

TCP 这么复杂,是因为要保证 可靠性,同时又尽可能的提高性能。

可靠性
校验和
序列号(按序到达)
确认应答
超时重发
连接管理
流量控制
拥塞控制
提高性能:
滑动窗口
快速重传
延迟应答
捎带应答
其他:
定时器(超时重传定时器, 保活定时器, TIME_WAIT定时器等)

另,基于TCP应用层协议有如下这些:

  • HTTP
  • HTTPS
  • SSH
  • Telnet
  • FTP
  • SMTP
    当然, 也包括我们自己写 TCP 程序时自定义的应用层协议

🥰如果本文对你有些帮助,请给个赞或收藏,你的支持是对作者大大莫大的鼓励!!(✿◡‿◡) 欢迎评论留言~~


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

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

相关文章

【Python】基于OpenCV人脸追踪、手势识别控制的求实之路FPS游戏操作

【Python】基于OpenCV人脸追踪、手势识别控制的求实之路FPS游戏操作 文章目录 手势识别人脸追踪键盘控制整体代码附录&#xff1a;列表的赋值类型和py打包列表赋值BUG复现代码改进优化总结 py打包 视频&#xff1a; 基于OpenCV人脸追踪、手势识别控制的求实之路FPS游戏操作 手…

小谈设计模式(7)—装饰模式

小谈设计模式&#xff08;7&#xff09;—装饰模式 专栏介绍专栏地址专栏介绍 装饰模式装饰模式角色Component&#xff08;抽象组件&#xff09;ConcreteComponent&#xff08;具体组件&#xff09;Decorator&#xff08;抽象装饰器&#xff09;ConcreteDecorator&#xff08;具…

玩转数据-大数据-Flink SQL 中的时间属性

一、说明 时间属性是大数据中的一个重要方面&#xff0c;像窗口&#xff08;在 Table API 和 SQL &#xff09;这种基于时间的操作&#xff0c;需要有时间信息。我们可以通过时间属性来更加灵活高效地处理数据&#xff0c;下面我们通过处理时间和事件时间来探讨一下Flink SQL …

解决前端二进制流下载的文件(例如:excel)打不开的问题

1. 现在后端请求数据后&#xff0c;返回了一个二进制的数据&#xff0c;我们要把它下载下来。 这是响应的数据&#xff1a; 2. 这是调用接口的地方&#xff1a; uploadOk(){if(this.files.length 0){return this.$Message.warning("请选择上传文件&#xff01;&#xff…

小白vite+vue3搭建项目整个流程

第一步 查看npm 版本npm -v&#xff0c;npm版本是7&#xff0c;创建项目命令&#xff1a; npm create vitelatest threejsVue -- --template vue第二步 // 进入项目名为threejsVue的项目命令 cd threejsVue // 安装路由 npm install vue-router4 // 安装css npm install -D s…

十、空闲任务及其钩子函数

1、空闲任务的介绍 (1)一个良好的程序&#xff0c;它的任务都是事件驱动的&#xff1a;平时大部分时间处于阻塞状态。 (2)有可能我们自己创建的所有任务都无法执行&#xff0c;但是调度器必须能找到一个可以运行的任务。所以&#xff0c;我们要提供空闲任务。 (3)在使用vTas…

Python日期的加减等操作

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 日期输出格式化 所有日期、时间的api都在datetime模块内。 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视频教程、插件安装教程、资料我都…

揭秘:机构招生电子传单制作的五个黄金法则

机构招生微传单制作一直都是让很多人在意的事情。一款好的微传单不仅可以吸引更多的学生&#xff0c;还可以省去很多招生工作的时间和精力。但是&#xff0c;很多人却不知道如何制作一款精美的微传单。下面就让我们来学习一下如何制作一款机构招生的微传单吧。 首先&#xff0c…

亲和力的作用,以及提高亲和力的六个办法

亲和力指的是容易使人亲近&#xff0c;当人身处集体中&#xff0c;亲和力也即是影响力和凝聚力的体现。通常是在职场中会明确对亲和力的考评&#xff0c;尤其是某些管理型岗位&#xff0c;所以HR人力资源管理中对亲和力有详细的评级标准。不过这里小猫测试网不详细讨论亲和力的…

敏感性分析一览

敏感性分析 SobolMorrisFourier Amplitude Sensitivity Test (FAST)Random Balance Designs - Fourier Amplitude Sensitivity Test (RBD-FAST)Delta Moment-Independent MeasureDerivative-based Global Sensitivity Measure (DGSM)Fractional Factorial Sensitivity Analysis…

服务器数据恢复-zfs下raidz多块磁盘离线导致服务器崩溃的数据恢复案例

服务器数据恢复环境&#xff1a; 一台服务器共配备32块硬盘&#xff0c;组建了4组RAIDZ&#xff0c;Windows操作系统zfs文件系统。 服务器故障&#xff1a; 服务器在运行过程中突然崩溃&#xff0c;经过初步检测检测没有发现服务器存在物理故障&#xff0c;重启服务器后故障依…

YOLOv7改进:结合CotNet Transformer结构

1.简介 京东AI研究院提出的一种新的注意力结构。将CoT Block代替了ResNet结构中的3x3卷积&#xff0c;在分类检测分割等任务效果都出类拔萃 论文&#xff1a;Contextual Transformer Networks for Visual Recognition 论文地址&#xff1a;https://arxiv.org/abs/2107.12292 有…

技术Leader对下管理的法宝-SMART

SMART方法论 源于国外管理大师的《管理的实践》是管理者能够更加明确员工高效工作的利器&#xff0c;科学、规范的对员工绩效制定考核目标和考核标准5个单词缩写 Specific:目标要具体Measurable:目标成果要可衡量(量化) Attainable:目标要可实现&#xff0c;避免过高/过低Rel…

模块化CSS

1、什么是模块化CSS 模块化CSS是一种将CSS样式表的规则和样式定义封装到模块或组件级别的方法&#xff0c;以便于更好地管理、维护和组织样式代码。这种方法通过将样式与特定的HTML元素或组件相关联&#xff0c;提供了一种更具可维护性、可复用性和隔离性的方式来处理样式。简单…

SpringBoot读取配置的方式

在 Spring Boot 应用中&#xff0c;我们通常需要一些配置信息来指导应用的运行。这些配置信息可以包括如下内容&#xff1a;端口号、数据库连接信息、日志配置、缓存配置、认证配置、等等。Spring Boot 提供了多种方式来读取这些配置信息。读取配置的目的是为了在程序中使用这些…

前端JavaScript入门到精通,javascript核心进阶ES6语法、API、js高级等基础知识和实战 —— Web APIs(三)

思维导图 全选案例 大按钮控制小按钮 小按钮控制大按钮 css伪类选择器checked <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><…

Apache Commons Pool2 池化技术

对象池是一种设计模式&#xff0c;用于管理和重用对象&#xff0c;以提高性能和资源利用率。对象池的概念在许多应用程序中都有广泛应用&#xff0c;特别是在需要频繁创建和销毁对象的情况下&#xff0c;例如数据库连接、线程、HTTP连接等 对象池通过预先创建一组对象并将它们存…

B. Comparison String

题目&#xff1a; 样例&#xff1a; 输入 4 4 <<>> 4 >><< 5 >>>>> 7 <><><><输出 3 3 6 2 思路&#xff1a; 由题意&#xff0c;条件是 又因为要使用尽可能少的数字&#xff0c;这是一道贪心题&#xff0c;所以…

最大内切圆算法计算裂缝宽度

本文这里是对CSDN上另一位博主的代码进行了整理&#xff1a; 基于opencv的裂缝宽度检测算法&#xff08;计算轮廓最大内切圆算法&#xff09; 我觉得这位博主应该是上传了一个代码草稿&#xff0c;我对其进行了重新整理&#xff0c;并添加了详细的注释。 import cv2 import …

ICCV 2023|Occ2Net,一种基于3D 占据估计的有效且稳健的带有遮挡区域的图像匹配方法...

本文为大家介绍一篇入选ICCV 2023的论文&#xff0c;《Occ2Net: Robust Image Matching Based on 3D Occupancy Estimation for Occluded Regions》&#xff0c; 一种基于3D 占据估计的有效且稳健的带有遮挡区域的图像匹配方法。 论文链接&#xff1a;https://arxiv.org/abs/23…