TCP(传输控制协议,Transmission Control Protocol)是互联网的核心协议之一,它提供了可靠、有序、双向的字节流通信服务。
TCP 的特点
TCP 具有以下几个主要特点:
- 面向连接:TCP 是面向连接的协议,在数据传输前需要建立连接,确保通信双方的可靠连接。
- 可靠传输:TCP 提供可靠的数据传输服务,确保数据无差错、不丢失、不重复,且按序到达。
- 全双工通信:TCP 支持全双工通信,允许数据在两个方向上同时传输。
- 流量控制:TCP 使用滑动窗口机制进行流量控制,防止发送方发送过多数据导致接收方缓冲溢出。
- 拥塞控制:TCP 具有拥塞控制机制,通过调整发送速率来避免网络拥塞。
- 面向字节流:TCP 将应用程序的数据视为连续的字节流,而不是独立的数据包。
这些特点使得 TCP 在需要高可靠性与稳定性的网络通信中得到了广泛的应用。
TCP 报文格式
TCP 报文由首部与数据两部分组成。首部一般由 20-60 字节构成,长度可变。
源端口(Source Port):发送方的端口号。
目的端口(Destination Port):接收方的端口号。
序号(Sequence Number):用于标识数据的顺序。
确认号(Acknowledgment Number):表示期望收到的下一个数据字节的序号。
数据偏移(Data Offset):指示数据起始处距离报文段起始处的距离。
保留位(Reserved):暂时没有具体作用
控制位(Flags):包括 URG、ACK、PSH、RST、SYN、FIN 等,用于控制连接的建立、终止等。
- URG:紧急指针有效位,表示紧急指针字段有效。
- ACK:确认序号有效位,只有当 ACK=1 时,确认号字段才会有效。
- PSH:推送功能位。当 PSH=1 时,接收方应尽快将数据交给应用层。
- RST:复位连接位。当 RST=1 时,表示 TCP 连接中出现严重错误,需要重新建立连接。
- SYN:同步序号位。在建立连接时使用。当 SYN=1 且 ACK=0 时,表示这是一个连接请求报文段。
- FIN:终止连接位。当 FIN=1 时,表示发送方的数据已发送完毕,并要求释放连接。
注意:以上控制位是老版本 TCP 协议的规定,新版本添加了 CWR 与 ECE 两个控制位,相应的保留位减少为 4 位。
窗口大小(Window Size):用于流量控制。
**校验和(Checksum) **:用于确保数据的完整性。
紧急指针(Urgent Pointer):仅在 URG=1 时有效,指示紧急数据的字节数。
选项(Options):长度可变,最长可达 40 字节。
填充(Padding):确保 TCP 标头在 32 位边界上结束,数据在 32 位边界上开始。填充由零组成。
TCP 连接管理
连接建立
TCP 使用「三次握手」机制来建立连接。
三次握手(Three-way Handshake)机制的步骤如下图:
假设客户端与服务端都是从 CLOSED 状态开始。
- 服务端进程创建 TCB 并使用 TCB 准备接收客户端请求,TCB 生成后,服务端将状态更改为 LISTEN。
- 同样的,客户端创建一个 TCB 并使用这个 TCB 发送请求,在请求头中设置「SYN=1」,并初始化一个任意序号,
seq=x
。SYN 包(即 SYN=1)不能携带任何数据内容,但会消耗一个序号。发送请求之后,客户端进入 SYN-SENT 状态。 - 收到客户端请求后:
- 如果服务器接收此连接,它将发回确认响应。在响应中 SYN 与 ACK 都将设置为「1」,并且服务端也将初始化一个序号,
seq=y
。服务端将在数据包内携带这个序号,该序号用于对客户端的 SYN 数据包进行确认。这个数据包同样不能够携带任何数据内容,但它消耗一个序号。所以在这个数据包中seq=y, ack=x+1
。服务端进入 SYN-RCVD 状态。 - 如果服务端拒绝连接,它就只响应一个 RST 包来重置连接。
- 如果服务器接收此连接,它将发回确认响应。在响应中 SYN 与 ACK 都将设置为「1」,并且服务端也将初始化一个序号,
- 客户端收到服务端的响应之后,也会发回一个确认数据包,其中 ACK 位设置为 “1”,并且
seq=x+1, ack=y+1
。
之后双方都进入 ESTABLISHED 状态,三次握手结束,客户端与服务端完成连接的建立。
TCB – 传输控制块,类似于 PCB,它存储一些重要的信息,例如 TCB 连接表、发送和接收缓冲区的指针、重传队列指针、当前序号和确认号等信息。
客户端在第三步中可以向服务端发送数据内容,如果不带,该包不会消耗 SYN 号,所以确认包的序号是 seq=x+1,但在实际场景中 TCP 的三次握手不仅仅是发起连接,客户端和服务端之间会交换连接中需要的参数。
三次握手的步骤:
连接释放
TCP 使用「四次挥手」机制来断开连接。
四次挥手(Four-Way Handshake)机制的步骤如下图:
TCP 的连接终止之所以复杂,是因为在正常操作期间,两个设备同时发送和接收数据。通常,连接终止始于一个设备的进程向 TCP 表示它想关闭连接。另一设备上的匹配进程可能完全不知道其对等方想结束连接。需要几个步骤来确保两个设备优雅地关闭连接,并且在此过程中没有数据丢失。
最终,关闭 TCP 连接需要连接两端的应用进程都认识到连接即将结束,并停止发送数据。因此,连接终止的实现方式是每个设备分别终止其连接的一端。一个设备关闭连接的行为意味着该设备将不再发送数据,但可以继续接收数据,直到另一设备决定停止发送。这允许通信双方的所有待发送数据在连接结束前被刷新。
在正常情况下,每一方通过发送一个设置了 FIN(完成)位的特殊消息来终止其连接端。这个消息,有时称为 FIN,作为对另一设备的连接终止请求,同时也可能像常规段一样携带数据。接收 FIN 的设备通过发送对 FIN 的确认(ACK)来响应,以表明已收到该消息。只有当双方都完成了通过发送 FIN 和接收 ACK 的关闭过程后,整个连接才被认为终止。
因此,TCP 终止连接不像建立连接那样是三次握手:它是两对两次握手。在正常连接关闭期间,连接中的两个设备所经历的状态是不同的,因为发起关闭的设备必须与接收终止请求的设备的行为不同。特别是,接收初始终止请求的设备上的 TCP 必须通知其应用进程,并等待进程准备好继续的信号。发起设备不需要这样做,因为应用程序是最初启动关闭过程的。
TCP 连接通常使用一种特殊的过程终止,其中每一方独立关闭其端的链接。通常由其中一个应用程序进程向其 TCP 层发出信号,表示会话不再需要。该设备发送一个 FIN 消息,告诉另一设备它想结束连接,并得到确认。当响应设备准备好时,它也发送一个 FIN 消息,并得到确认;在等待一段时间以接收 ACK 后,会话关闭。
客户端与服务端都以 ESTABLISHED 状态开始,现在客户端想要终止 TCP 连接。
- 当需要进行连接释放时,客户端发送一个携带 FIN 标志的 TCP 报文段。发送之后,客户端状态更改为 FIN-WAIT-1。
- 服务端收到客户端的 FIN 报文段后,发送一个带有 ACK 标志的 TCP 报文段,表明已经收到了客户端的 FIN 请求。发送之后,服务端进入 CLOSE-WAIT 状态。客户端收到来自服务端的 ACK 之后,进入 FIN-WAIT-2 状态。
- 服务端在准备好关闭连接后,发送一个带有 FIN 标志的 TCP 报文段,表明服务端也希望释放连接,此时,服务端进入 LAST-ACK 状态。
- 客户端收到服务端的 FIN 报文段后,发送一个带有 ACK 标志的 TCP 报文段,确认收到服务端的 FIN 请求。此时,客户端进入 TIME-WAIT 状态,等待一段时间(通常为 2MSL,最大报文段生成时间的两倍)以确保服务端收到 ACK 报文段后再进入 CLOSED 状态。服务器在收到 ACK 报文段后立即进入 CLOSED 状态。
四次挥手的步骤:
TCP 流量控制
TCP 流量控制是一种机制,旨在确保发送方不会淹没接收方的能力。它通过使用滑动窗口协议来实现,窗口大小动态调整,反映接收方的缓冲区容量。接收方会定期发送确认(ACK)消息,告知发送方当前的窗口大小,防止数据丢失和拥塞。
流量控制与拥塞控制不同,后者主要关注网络的整体流量状态。
在连接的 TCP 设备之间发送数据是通过「滑动窗口」系统来实现的,发送端和接收端均有一个滑动窗口,对应一个缓冲区,记录当前发送或接收到的数据。接收端会在返回的ACK报文中包含自己可用于接收数据的缓冲区的大小,在 TCP 的报文首部里用 Window Size
表示,16 bit,最多 65535 字节。发送端发送的数据不会超过 Window Size
的大小。
发送方根据接收方通知的窗口大小来确定发送数据的数量,如果窗口大小为 0,则发送方暂停发送数据,等待接收方处理完数据后再次通知窗口大小。
TCP 可靠性机制
超时重传
TCP 使用超时重发的重传机制。 即:TCP 每发送一个报文段,就对此报文段设置一个超时重传计时器。
此计时器设置的超时重传时间 RTO(Retransmission Time-Out)应当略大于 TCP 报文段的平均往返时延 RTT,一般可取 RTO=2RTT。 但是,也可以根据具体情况人为调整 RTO 的值,例如可以设置此超时重传时间 RTO=90 秒。 当超过了规定的超时重传时间还未收到对此 TCP 报文段的预期确认信息,则必须重新传输此 TCP 报文段。
序列号与确认应答机制
TCP 的序列号和确认应答机制是其可靠性的重要组成部分。
TCP 将数据流分割成小的数据段,每个数据段都有一个唯一的序列号,用于标识数据的顺序。接收方根据序列号将数据重新组装,确保数据按照正确顺序处理。接收方在成功接收数据段后,发送一个 ACK 报文,ACK 包含下一个期望接收的数据段的序列号。TCP 使用累计确认方式,即确认所有已接收的数据段,直到某一特定序列号的数据段。例如,如果接收方收到序列号 1 到 5 的数据段,但 6 未到达,它会发送 ACK 为 6,表示期望接收序列号 6的数据。
校验和
TCP 的校验和是一种用于检测数据在传输过程中是否发生错误的机制。
在发送数据时,TCP 会对 TCP 段的内容(包括头部和数据)进行校验和计算,在计算校验和时,还会使用一个虚拟头部,其中包含源 IP 地址、目标 IP 地址、协议号(TCP 协议为 6)和 TCP 段的长度。
计算得到的校验和值会被放置在 TCP 头部的相应字段中,作为数据段的一部分发送给接收方。
接收方在收到 TCP 段后,会对数据段进行相同的校验和计算。如果计算得到的校验和与接收到的校验和相符,说明数据未发生错误;如果不符,则表示数据在传输中出现了错误。
接收方如果发现校验和不匹配,会丢弃该数据段,不会发送确认应答。
TCP 拥塞控制
拥塞控制是 TCP 协议的一部分,旨在动态调整数据传输的速率,以适应网络的当前状态。它通过监测网络条件来决定发送数据的速度。
发送端维持一个叫做拥塞窗口 cwnd
的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态变化。
TCP 拥塞控制主要通过以下几个算法来实现:
慢启动
在连接开始时,TCP 设置拥塞窗口(Congestion Window, CWND)为一个小值(通常为 1 或 2 个 MSS)。每收到一个确认(ACK),CWND 就增加 1(以 MSS 为单位),则每经过一个 RTT(往返时间),CWND 会翻倍,这使得窗口的大小呈指数增长。这个算法可以快速探测网络的可用带宽。
当 CWND >= ssthresh(慢启动阈值)时,进入拥塞避免阶段。
拥塞避免
当 CWND 达到一个阈值(ssthresh)时,TCP 进入拥塞避免阶段。在这个阶段,每经过一个 RTT,CWND 会增加一个 MSS,而不是以指数方式增长。这个算法可以减少网络拥塞的风险。
快速重传
如果 TCP 发送的数据包丢失,接收端会多次发送相同的数据包的 ACK。发送端在收到三个重复的 ACK 时,会立即重传丢失的数据包,而不是等待重传计时器到期。这个算法可以提高丢包恢复的速度。
快速恢复
在快速重传后,TCP 不会立即进入慢启动,而是将 ssthresh 设置为 CWND 的一半,然后在每次收到 ACK 时将 CWND 增加 1。这个算法可以在数据包丢失的情况下迅速恢复,而不完全放弃当前的传输状态。
TCP 延迟和效率
Nagle 算法
TCP Nagle 算法的工作原理主要是通过控制小数据包的发送来减少网络拥塞和提高传输效率。
在TCP协议中,每个数据包的发送都需要一定的网络资源,包括带宽和处理能力。发送大量的小数据包会导致网络拥堵,并增加网络延迟。Nagle算法旨在解决这个问题,尤其是在交互式应用(如远程终端或在线游戏)中,频繁发送小数据包会显著影响性能。
工作原理
Nagle 算法会合并小的数据包,当应用程序试图发送一个小于一个完整 TCP 报文段(通常为 1460 字节)的数据包时,Nagle 算法不会立即发送这个数据包。它会将这些小数据包缓存,并等待一个时间间隔(通常是 100 毫秒),或者直到发送的数据量达到一个完整的 TCP 报文段。
Nagle 算法还使用等待确认机制,Nagle 算法还会在收到对之前发送的数据包的确认(ACK)后,才会发送缓存中的数据。这意味着如果前一个数据包尚未得到确认,新的小数据包将不会被发送。
Nagle 算法可以通过 TCP socket 的选项进行启用或禁用。某些实时应用(如 VoIP 或在线游戏)可能需要禁用该算法,以减少延迟并实现更快的交互。
优点与缺点
优点:降低网络拥塞,通过合并小数据包,减少 TCP 连接中的包数量,减轻网络负担。提高带宽利用率,减少了因为小包导致的带宽浪费。
缺点:增加延迟,在需要快速响应的场景中(如实时游戏或输入响应),Nagle 算法可能导致延迟,因为它在发送数据时会等待。复杂性,应用程序需要考虑何时禁用该算法,以适应特定的性能需求。
适用场景
适合用于一般的数据传输应用,如文件传输、邮件等。不适合实时性要求高的应用,如在线游戏、实时语音和视频通话等。
延迟确认
TCP 延迟确认(Delayed Acknowledgment)是一种优化技术,旨在减少 TCP 连接中的 ACK(确认应答)报文数量,从而降低网络负担和提高效率。
工作原理
在正常情况下,TCP 接收方在收到数据包后,会立即发送 ACK 来确认收到的数据。延迟确认机制允许接收方在收到数据包后,延迟发送 ACK,通常是在一定的时间(例如 200 毫秒)内等待是否会收到更多的数据包。
如果在延迟时间内接收方收到了新的数据包,它会将当前的 ACK 和新的数据包一起返回,从而减少 ACK 的数量。这意味着接收方可能在同一 ACK 中确认多个数据包。
通过减少 ACK 的数量,延迟确认可以降低网络上的拥塞,尤其是在高延迟网络环境中。
优点与缺点
优点:减少 ACK 数量,降低了网络中的小数据包数量,减轻了网络负担。提高带宽利用率,通过合并 ACK,可以更高效地利用带宽。
缺点:增加延迟,在某些情况下,延迟确认可能会引入额外的延迟,影响实时应用的性能。可能导致 TCP 窗口的减小,在高延迟网络中,如果 ACK 延迟发送,发送方可能会等待更长时间来发送后续数据,从而减小 TCP 窗口的利用率。
适用场景
延迟确认通常在文件传输、电子邮件等对实时性要求不高的应用中使用。在需要实时响应的应用(如在线游戏、实时视频通话等)中,延迟确认可能会引入不必要的延迟,因此不推荐使用。
窗口缩放
TCP 窗口缩放(TCP Window Scaling)是一种扩展 TCP 协议的机制,旨在解决在高带宽-延迟网络环境中 TCP 窗口大小的限制问题。
TCP 窗口大小是指发送方在未收到确认的情况下可以发送的最大字节数。标准 TCP 协议的窗口大小限制在 64KB(65535字节),这在现代高带宽网络中可能成为瓶颈,导致数据传输效率低下。
工作原理
TCP 窗口缩放通过在 TCP 头部引入一个“窗口缩放”选项,使得窗口大小可以扩展到更大的值。这个选项在 TCP 握手(三次握手)期间协商。
窗口缩放使用一个缩放因子(shift count),表示窗口大小可以乘以 2 的幂来扩大。例如,如果缩放因子为 3,那么实际窗口大小为协商窗口大小乘以8($2^3$)。
使用窗口缩放后,TCP 连接的窗口大小可以扩大到最大 1GB($2^{30}$字节),这适用于高速网络,允许发送更多未确认的数据,从而提高效率。
优点与缺点
优点:提高吞吐量,在高带宽-延迟环境中,可以有效地提高数据传输的吞吐量,减少等待时间。优化网络利用率,允许更大的数据流,提高了 TCP 连接的性能。
缺点:复杂性增加,在实现和调试过程中,窗口缩放可能增加复杂性,特别是在不同设备之间的兼容性问题。需要支持,所有参与 TCP 连接的设备都必须支持窗口缩放,才能有效使用。
适用场景
在高带宽、长延迟的网络环境(如跨国数据传输、大规模数据备份等)中,窗口缩放可以显著提高性能。对于低带宽或延迟较小的网络,可能没有显著的益处。
TCP 应用场景
适用于需要可靠传输的应用,如文件传输(FTP)、邮件(SMTP)、网页浏览(HTTP/HTTPS)等。不适用于实时性要求高但容错性较强的应用,如 VoIP、在线游戏等(这些场景通常使用UDP)
TCP 与其他协议的比较
UDP(用户数据报协议)是无连接的、不可靠的,适用于实时应用。QUIC 是谷歌开发的基于 UDP 的新一代传输协议,集成了 TLS、优化了连接建立时间。