TCP 协议

TCP 协议通过确认应答(ACK)机制、超时重传和连接管理来实现可靠性。三次握手确保连接建立,防止因网络问题导致的错误连接。超时重传机制在数据未收到应答时进行重发,保证数据完整。连接管理包括TCP的三次握手和四次挥手,确保数据传输前后连接的正确建立和关闭。此外,TCP还使用滑动窗口、流量控制和拥塞控制策略提升性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


TCP 协议是传输层上一个非常重要的协议, 即: 传输控制协议

1. TCP 协议端格式

在这里插入图片描述

  • 源 / 目的端口号: 表示数据是从哪个进程来, 到哪个进程去
  • 32 位序号 / 32 位确认号: 保证 TCP 可靠传输的一种机制
  • 序号(SN): 用于对字节流进行编号:
    例如: 序号为 301, 表示第一个字节的编号为 301, 如果携带的数据长度为 100 字节, 则下一个报文段的编号就应该为 401
  • 确认号(ASN): 希望收到的下一个报文段的编号
    例如: B 收到 A 发来的一个报文段序号为 501, 携带的数据长度为 200 字节, 则 B 期待下一个收到的报文编号为 701, 所以 B 发送给 A 的确认报文段中确认序号就为 701
  • 4 位首部长度: 表示该 TCP 首部有多少个字节, 通常情况下是 4 个字节. 如果该选项为空, 则 TCP 首部为 20 个字节. 如果该选项不为空且为最大, 则 TCP 首部为 60 个字节 (TCP 最大首部长度就是 60 个字节)
  • 6 位标志位:
    • UGR: 紧急指针是否有效
    • ACK : 确认号是否有效
      ACK 只有两种情况: 0: 不具备应答能力; 1. 具备应答能力
    • PSH: 提醒接收端应用程序立刻从 TCP 缓冲区把数据读走
    • RST: 对方要求重新建立连接, 我们把携带 RST 标识的称为 复位报文段
    • SYN: 请求建立连接, 我们把携带 SYN 标识的称为 同步报文段
    • FIN: 通知对方, 本端要关闭了, 我们称携带 FIN 标识的为 结束报文段
  • 16 位窗口大小:
  • 16 位检验和: 和 UDP 的检验方法相同, 使用 CRC 检验, 但此处的检验, 不光包括 TCP 首部, 也包括 TCP 数据部分
  • 16 位紧急指针: 标识哪部分数据是紧急数据

2. TCP 可靠传输

2.1 什么是可靠性

可靠性是指:

  1. 通过 TCP 发送的数据, 能保证对方一定收到
  2. TCP 只能尽全力保证数据可以正确的发送给对方, 但它并不能完全保证数据一定能发送给对方
    ①. 数据会尽量发送给对方, 即使发送失败了, 应用层也可以知道数据发送失败的情况
    ②. 尽量保证数据会按照发送方的顺序到达接收方
    ③. TCP 会选择合适的数据大小进行发送
    ④. 由于 TCP 会做重传的操作, 所以有时候会出现接收方接收到重复的数据, 但这种情况下, TCP 会进行相应的处理

2.1.1 保证可靠性方法 — 确认应答(ACK)机制

  1. 什么是确认应答(ACK)机制:
    当发送方发送数据之后
    ①. 如果在超时时间(大概一个往返的时间)内, 发送方收到了接收方的应答, 就代表对方收到了数据
    ②. 如果在超时时间到达后, 发送方没有收到接收方的应答, 就代表对方没有收到数据
  2. 确认应答机制 如图所示:
    在这里插入图片描述
    TCP 将每个字节的数据都进行了编号(序号 SN), 当 主机A 向 主机B 发送了SN = 1, 数据长度为 1000 的数据后, 主机B 收到了 主机A 发来的数据, 会在超时时间内向 主机A 发起响应, 即 ACK = 1, 确认序号 ASN = 1001 (代表希望下一个被发送数据的序号为 1001), 这也意味着, 前 1000 个数据已经全部收到
  3. 为什么需要 确认应答机制?
    TCP 建立连接后, 发送的每一条数据都有可能丢失, 因此需要确认应答机制, 保证数据的完整性
  4. 确认应答机制的作用?
    ①. 可以确认接收端已经收到数据了
    ②. 可以确认接收端收到的是哪条数据

2.2.2 保证可靠性方法 — 超时重传机制

  1. 什么是超时重传机制?
    主机 A 在给主机 B 发送数据的时候, TCP 缓冲区会保留这份数据, 无论是发送的数据报丢失, 还是主机 B 给主机 A 发送的确认应答丢失, 只要主机 A 在一定的时间内没有收到来自主机 B 的确认应答, 主机 A 都会认为数据丢失, 从而会再向主机 B 发送一份刚刚的数据
  2. 如果是因为主机 B 发送的确认应答丢失, 这时主机 A 发来重复的数据, 该怎样解决?
    因为每个字节的数据都有自己的序号SN, 如果主机 B 收到了重复的数据, 就把重复的数据丢掉, 继续向主机 A 发送确认应答以及确认序号 ASN, 从而达到去重的效果
  3. TCP 为了保证高性能通信, 怎样确定超时时间?
    ①. 超时以 500ms 为一个单位进行控制, 每次判定超时后重发的超时时间都是 500ms 的整数倍
    ②. 如果重发一次后, 仍然没有收到应答, 就延长超时时间为 2 * 500ms, 如果第二次重发, 仍然没有收到应答, 延长超时时间为 4 * 500ms, 以此类推
    ③. 累计到一定的重传次数后对方仍然没有应答, 说明数据确实发不过去了, 就不再发送, 同时通知应用层发送失败
  4. 一份应答, 还有必要在进行应答吗?
    不需要在进行应答了, 因为应答本身不包含任何有效数据, 所以不需要在进行应答

2.2.3 保证可靠性方法 — 连接管理机制

由于 TCP 不单单为一个主机的应用层服务,. 所以, TCP 内部会有多条通信通道. 因此, TCP 内部管理各自通道中的这些数据, 就被抽象为连接

  • TCP 如何判断一份数据是交给哪个连接进行处理的?
    由于一个连接的唯一标识就是 五元组信息, 即 TCP, 本地 ip, 本地 port, 远端 ip, 远端 port
2.2.3.1 TCP 三次握手(检查网络连接状态, 协商一些关键性数据)

TCP 是面向连接的, 无论是哪一方在向另一方发送数据之前, 都需要建立一条连接
正常情况下, TCP 要经过 3 次握手建立连接
在这里插入图片描述
TCP 三次握手过程上图的上半部分:

  1. 客户端首先向服务端发送了一个 SYN 请求建立连接, 随后便进入到 SYN_SENT 的状态
  2. 服务端收到了 SYN 之后, 会立刻向客户端响应一个 ACK 确认应答, 同时也会发送一个 SYN 请求, 随后便进入到 SYN_RECVD 的状态
  3. 当客户端收到了 ACK + SYN 后, 会立即响应服务端一个 ACK 确认应答, 并进入到 ESTABLISHED 状态
  4. 服务端收到了 ACK 之后, 也会进入到 ESTABLISHED 的状态
  5. 当客户端和服务端都进入到了 ESTABLISHED 状态之后, 三次握手建立完毕, 客户端和服务端之间就可以进行互相通信了
①. 为什么是三次握手? 而不是两次?

三次握手的作用就是双方都可以明确自己和对方的收, 发能力是否正常
客户端第一次发送 SYN 给服务端时, 可能因为网络原因而阻塞, 当服务端接收到来自客户端的 SYN 时, 有可能客户端已经退出了. 如果只有两次握手, 那么服务端给客户端发送 ACK 响应之后就默认客户端具有接受能力且连接建立成功, 那么后续就可能出现错误, 所以一定需要客户端再次给服务端一个确认

②. 三次握手失败怎么办?(通常情况下, 是第三次握手失败)

如果服务端等不到来自客户端的 ACK 确认应答, 就会向客户端发送一个 RST 报文, 要求客户端重新建立连接, 并销毁刚才服务器创建的 Socket

2.2.3.2 TCP 四次挥手

TCP 四次挥手过程如上图的下半部分:

  1. 客户端首先向服务端发送一个 FIN 包, 请求关闭连接, 并进入到 FIN_WAIT1 状态
  2. 服务端收到来自客户端的 FIN 包后, 会向客户端发送一个 ACK 确认应答, 并进入到 CLOSE_WAIT 的状态
  3. 客户端收到了服务端的 ACK 后, 就会进入到 FIN_WAIT2 的状态
  4. 随后服务端也会向客户端发送一个 FIN 包, 并进入到 LAST_ACK 状态
  5. 客户端收到服务端的 FIN 包后, 会向服务端发送一个 ACK 确认应答, 并首先进入到 TIME_WAIT 的状态, 过一阵子再进入 CLOSED 状态
  6. 服务端收到客户端的 ACK 后, 直接进入到 CLOSED 状态
①. 为什么客户端最后一次向服务端发送 ACK 后会先进入 TIME_WAIT 的状态, 过一阵子才进入 CLOSED 状态呢?

客户端收到服务端的 FIN 包并响应 ACK 后就会进入到 TIME_WAIT 的状态(主动关闭的一方会进入到 TIME_WAIT 的状态), 此时并不是直接进入 CLOSED 的状态, 还需要等待一个时间计时器设置的时间 2MSL.(MSL: 报文最大生存时间), 这样做的理由是:

  • 确保最后一个 ACK 确认报文可以到达服务端. 如果服务端没有收到客户端的 ACK 报文, 那么服务端就会重新向客户端发送 FIN 包, 客户端等待一段时间就是为了处理这样的情况发生
  • 等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失, 使得下一个新的连接不会出现旧连接的请求报文
②. 如果服务器为主动关闭的一方, 服务器出现大量的 TIME_WAIT 状态是什么原因? 该怎样解决?

高并发短连接的 TCP 服务器上, 当服务器处理完请求后立刻按照主动正常关闭连接这个场景下, 就可能出现大量的 socket 处于 TIME_WAIT 的状态.
解决方法:

  • 应该尽量避免频繁关闭连接
  • 减少 TIME_WAIT 的等待时间(缩短 MSL 时间)
③. 如果服务端为被动关闭的一方, 服务端出现大量的 CLOSE_WAIT 状态是什么原因?

可能是忘记调用 socket.close(). (只有服务端调用了 close(), 才会向客户端发送 FIN 包)

2.2.3.3 为什么 TCP 挥手需要四次?

为什么是四次挥手:
如果客户端作为主动关闭的一方, 它在向服务端发送 FIN 包之前发送了大量的数据给服务端, 那么当服务端收到 FIN 包后会先发送一个 ACK 给客户端, 然后再对之前客户端发送的大量数据进行处理, 处理完毕之后, 再给客户端发送 FIN 包请求关闭连接, 然后再等待客户端响应 ACK

但是通过抓包处理, 发现服务端是将 ACK 和 FIN 包一起发送给客户端的, 这是因为: 服务端接收到客户端的 FIN 包之后不会立刻响应 ACK, 而是采用了延迟应答机制, 服务端先处理完数据之后, 在向客户端发送 FIN 包的时候采用捎带应答的机制把之前的 ACK 也返回给客户端. 这样做的好处是: 减少了网络阻塞的概率

2.2 提高性能的优化策略

2.2.1 滑动窗口机制

窗口大小其实就是一个缓冲区, 在这个缓冲区中的数据, 不需要等待确认应答也可以继续发送
当发送方窗口中的数据全部发送完毕之后, 就需要等待接收方的 ACK 确认应答, 只有等到了接收方的 ACK 之后, 发送方的窗口才可以向后移动, 移动至没有收到 ACK 应答的字节即可. 所以, 发送方在收到确认应答之前, 必须在缓冲区保留这写数据
接收方的窗口也是如此. 只有确定发送方收到了 ACK 之后, 才可以进行滑动窗口.

如果在数据传输的途中, 出现了丢包的现象, 如何进快速重传?
滑动窗口机制中的快速重传机制:

  • 如果数据包已经抵达, 但是 ACK 丢失了:
    这种情况下, 部分 ACK 丢失是没有关系的, 可以通过后续 ACK 进行确认
  • 如果数据包直接丢失了:
    接收端在接收数据的时候, 如果第一条数据没有收到却直接收到了第二条数据, 则接收端认为第一条数据丢失, 就会立刻向发送方发送重传第一条数据的请求, 如果发送方连续三次收到了重传数据的请求, 就会对这条数据进行重传
    例如: 发送方先给接收方发送了 0 - 1000 的数据, 接收方收到后会回复 1001 这样的 ACK, 然后发送放继续发送 1001 - 2000 的数据和 2001 - 3000 的数据, 如果此时接收方收到的是 2001 - 3000 的数据, 则认为 1001 - 2000 的数据丢失, 就会给发送方不断的发送 1001 这个 ACK, 当发送方连续三次收到 1001 这个 ACK, 就会给接收方再次重传 1001 - 2000 的数据.

2.2.2 流量控制

  1. 什么是流量控制机制:
    由于接收端的接收速度是有限的, 如果发送端的发送速度太快, 接收端的缓冲区很快就填满了, 这时如果发送端继续发送, 将会产生接收端丢包, 从而引起丢包重发等一系列问题
    因此 TCP 根据接收端的接收速度和接受能力对发送端的发送速度进行控制, 这样的一种机制就称为 流量控制机制
  2. 怎样进行流量控制?
    ①. 接收端通过 TCP 首部的 “窗口大小” , 用来传递并告知发送端自己的接受能力
    ②. TCP 通过滑动窗口机制来控制发送窗口的发送速度

2.2.3 拥塞控制

根据网络当前拥堵情况, 动态调节发送端的发送量
滑动窗口的大小是由接收窗口和拥塞窗口一起控制的, 最终发送窗口最大的发送量应该取接收窗口大小和拥塞窗口大小的最小值

慢启动, 快增长机制 (胆小的老鼠机制)
在这里插入图片描述

发送开始的时候, 定义拥塞窗口大小为 1, 一开始, 拥塞窗口大小是以指数形式增长的, 但是当拥塞窗口大小到达某一阈值时, 拥塞窗口的大小就开始以线性方式增长
一旦网络发生阻塞(大量丢包), 拥塞窗口又会重新变成 1, 同时阈值大小变成原来的 1/2, 然后继续实行慢启动, 快增长机制

3. 延迟应答

当接收方接收到数据之后, 不是立刻就进行确认应答, 而是等一段时间再进行确认应答.

由于窗口越大, 网络的吞吐量就越大, 传输效率就越高
例如: 接收端的窗口大小为 1M, 发送方发来了 500K 的数据, 如果接收端立刻应答, 则它的窗口大小就只有 500K 了. 但是如果处理端处理速度很快, 接收端稍微等一会, 处理端就可以将这 500K 的数据全部处理掉, 这时接收端再去响应发送端时的接收窗口大小就还是 1M

并不是可以无限延迟的, 延迟应答的策略:

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

4. 捎带应答

当接收端接收到发送方发来的数据后, 并不是立刻就发送 ACK, 而是等数据处理完毕后, 将数据和 ACK 一起发送给发送方

5. TCP 粘包问题

TCP 的粘包问题可能会发生在发送端, 也可能会发生在接收端

  • 发送端: 发送的时候是将数据放在缓冲区的, 可能放了很多的数据之后才发送给接收端, 这时可能会产生粘包问题
  • 接收端: 发送端发来的数据, 接收端没来得及接收, 这时发送端再次发来数据时, 就可能产生粘包问题

如何避免粘包问题:

  • 对于定长的包, 每次都按照固定大小读取
  • 对于变长的包:
    • 可以在包头的位置约定一个包的长度, 这样就可以知道包的结束位置
    • 可以在包和包之间, 使用明确的分割符

UDP 协议中是否会产生粘包问题?
UDP 协议中是不会产生粘包问题的, 因为:

  • 使用 UDP 协议的时候, 数据都是整条交付给应用层的, 所以不会产生粘包现象
  • 在 UDP 协议中, 是指定了 UDP 长度的, 所以不会产生粘包现象

6. TCP 异常情况

进程终止, 机器关机, 重启都会释放文件描述符, 仍然可以发送 FIN 包, 和正常关闭没有什么区别
机器掉电 / 网线断开: 一开始, 接收端会认为连接还在, 一旦接收方给发送方发送消息, 发送方一直未回复, 持续一段时间后, 一直超时, 接收方就会发现连接已经不在了, 于是会进行 reset 操作. 即使接收方没有给发送方发送消息, 接收方也会定时询问发送方还在不在, 如果多次询问也没有得到回应, 接收方就默认发送方不在了, 也会断开连接.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值