【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.mzph.cn/news/87985.shtml

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

相关文章

在B站上如何把已经上传的视频做成合集?

参考视频: 【在B站上如何把已经上传的视频做成合集&#xff1f;】 https://www.bilibili.com/video/BV1Uf4y1G7eR/?share_sourcecopy_web&vd_source8af85e60c2df9af1f0fd23935753a933 【B站投稿视频合集的几种方式最全攻略】 https://www.bilibili.com/video/BV1jZ4y1h7…

SpringCloud 学习(三)Ribbon 和 Feign

4. Netflix.Ribbon 4.1 简介 (1) 概念 Spring Cloud Ribbon 是基于 Netflix Ribbon 实现的一套客户端负载均衡工具。 (2) 负载均衡&#xff08;LB&#xff1a;LoadBalance&#xff09;和集群架构 应用集群&#xff1a;将同一应用部署到多台机器上&#xff0c;组成处理集群&…

Android逆向技术高阶大法

原文链接 Android逆向技术高阶大法 安卓应用是一个客户端&#xff0c;与传统软件类似&#xff0c;需要把软件打包&#xff0c;然后通过某种渠道&#xff08;应用市场&#xff09;分发给用户&#xff0c;这是常规的发布方式&#xff0c;它的更新节奏很慢&#xff0c;从你在应用…

Docker 容器监控之CAdvisor+InfluxDB+Granfana

是什么 一句话&#xff1a;CAdvisor监控收集InfluxDB存储数据Granfana展示图表 CAdvisor InfluxDB Granfana 总结 容器编排CIG CIG CAdvisorInfluxDBGranfana 1、新建目录 2、新建docker-compose.yml文件 version: 3.1volumes:grafana_data: {}services:influxdb:image: t…

C语言实现八种功能的通讯录(添加、删除、查找、修改、显示、排序、退出、清空)

通讯录功能概要及前提说明 此通讯录利用C语言完成&#xff0c;可以实现八种功能的通讯录&#xff08;添加、删除、查找、修改、显示、排序、退出、清空&#xff09; 代码由三部分组成&#xff0c;为什么要写成三部分而不写成一部分可以参考我以前的博客&#xff0c;如下&…

【PMP/软考】软件需求的三个主要层次:业务需求、用户需求和功能需求解释及实例解析

简述 当进行需求分析时&#xff0c;通常着重考虑三个主要层次&#xff1a;业务需求、用户需求和功能需求。业务需求关注项目与组织战略目标的一致性&#xff0c;用户需求明确最终用户的期望&#xff0c;而功能需求定义具体的系统功能和特性。这三个层次为项目管理和软件工程提…

基于改进莱维飞行和混沌映射的粒子群优化BP神经网络预测股票价格研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

[架构之路-223]:数据管理能力成熟度评估模型DCMM简介

目录 一、背景 二、评估依据 三、评估内容 四、主要适用对象 五、能力等级 六、不同层次的文件&#xff1a; 一、背景 信息技术与经济社会的交汇融合引发了数据爆发式增长。数据蕴含着重要的价值&#xff0c;已成为国家基础性战略资源&#xff0c;正日益对全球生产、流通…

更适合程序员体质的PPT制作工具——Slidev

Slidev简介 Slidev是什么 Slidev是一款基于Vue.js的现代化幻灯片制作工具&#xff0c;它可以帮助用户快速、高效地制作出美观、专业的幻灯片。 目前市面上有很多功能丰富的、通用的、所见即所得的幻灯片制作工具&#xff0c;例如 微软 PowerPoint 或 苹果 Keynote. 它们在制…

tp8 Editor.md

Editor.md - 开源在线 Markdown 编辑器 放于public文件夹下 html代码&#xff1a; <div class"layui-col-md12" id"content"><textarea name"content" placeholder"详情" class"layui-textarea">{notempty nam…

buuctf web [极客大挑战 2019]Upload

上传头像&#xff0c;上传一下&#xff0c;看看能不能成功 抓包&#xff0c;抓取上传时的数据,看看限制条件 改两个地方&#xff0c;符合上传图片的要求&#xff0c;上传试试 一句话木马的<?被扳了 改一下木马的格式 <script language"php">eval($_POST[cm…

Tomcat 开启远程调试

Tomcat 部署的 war包工程开启远程调试 Linux服务器下&#xff0c;编辑Tomcat bin 目录下的 startup.sh 文件 vim startup.sh在第一行加入&#xff1a;(不换行&#xff0c;在同一行) declare -x CATALINA_OPTS"-server -Xdebug -Xnoagent -Djava.compilerNONE -Xrunjdwp:…

Redis 集群搭建教程

一、介绍 Redis 集群有着高可用、易扩展、更好的性能等优势&#xff0c;本文主要是实战搭建一个三主三从的 Redis 集群。 正常来说&#xff0c;搭建 Redis 集群需要 6 台服务器。为了简单一点&#xff0c;本文通过一台服务器&#xff0c;6 个端口&#xff0c;搭建一个 Redis …

孜然单授权系统V1.0[免费使用]

您还在为授权系统用哪家而发愁&#xff1f;孜然单授权系统为您解决苦恼&#xff0c;本系统永久免费。 是的&#xff0c;还是那个孜然&#xff0c;消失了一年不是跑路了是没有空&#xff0c;但是这些都是无关紧要的&#xff0c;为大家带来的孜然单授权系统至上我最高的诚意&…

数据安全态势管理:什么是事实,什么是虚构?

考虑到组织存储大量数据的日益复杂的云环境&#xff0c;数据安全态势管理 ( DSPM )的兴起并不令人意外。使组织能够全面了解云数据资产和敏感数据的安全状况的流程对于当今的安全团队来说非常有价值。 尽管 DSPM 的重要性日益凸显&#xff0c;但人们对于它能为企业做什么和不能…

序列化对象(ObjectOutputStream,ObjectInputStream)

1、对象序列化 作用&#xff1a;以 内存 为基准&#xff0c;把内存中的对象存储到磁盘文件中去&#xff0c;称为对象序列化使用到的流是对象字节输出流&#xff1a;ObjectOutputStream package com.csdn.d7_serializable; import java.io.*; public class ObjectOutputStreamDe…

电脑开机慢问题的简单处理

电脑用久了&#xff0c;开机时间要10-20分钟特别慢&#xff0c;一下介绍两种简单有效处理方式&#xff0c;这两种方式经测试不会影响原系统软件的使用&#xff1a; 方式一&#xff1a;禁用非必要启动项【效果不是很明显】 利用360里面的优化加速禁用启动项【禁用启动项还有其…

大数据Flink(八十九):Temporal Join(快照 Join)

文章目录 Temporal Join(快照 Join) Temporal Join(快照 Join) Temporal Join 定义(支持 Batch\Streaming):Temporal Join 在离线的概念中其实是没有类似的 Join 概念的,但是离线中常常会维护一种表叫做 拉链快照表,使用一个明细表去 join 这个 拉链快照表 的 join …

【LeetCode-简单题】501. 二叉搜索树中的众数

文章目录 题目方法一&#xff1a;暴力哈希方法二&#xff1a;利用二叉搜索树的特性&#xff08;递归双指针&#xff09; 题目 方法一&#xff1a;暴力哈希 这是针对于普通二叉树的解法 统计number出现次数 然后将次数最大的众数集 取出来 Map<Integer , Integer > map …

Mysql备份恢复、与日志管理

Mysql日志管理、备份与恢复 一、Mysql日志管理1.1、日志分类1.1.1、错误日志1.1.2 、通用查询日志1.1.3、 二进制日志1.1.4 、慢查询日志1.1.5 、配置日志 1.2、日志的查询 二、备份与恢复2.1、 数据备份的必要性2.2 、造成数据丢失的原因2.3、 数据库备份的分类2.3.1、 物理备…