TCP 协议学习总结
TCP 协议
传输控制协议(英语:Transmission Control Protocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由 IETF 的 RFC 793 定义。在简化的计算机网络 OSI 模型中,它完成第四层传输层所指定的功能。用户数据报协议(UDP)是同一层内另一个重要的传输协议。
TCP 用三次握手(或称三路握手,three-way handshake)过程创建一个连接。
在 TCP 的数据传送状态,很多重要的机制保证了TCP的可靠性和强壮性。它们包括:使用序号,对收到的 TCP 报文段进行排序以及检测重复的数据;使用校验和检测报文段的错误,即无错传输;使用确认和计时器来检测和纠正丢包或延时;流控制(Flow control);拥塞控制(Congestion control);丢失包的重传。
TCP 中几个重要的机制如下:
-
可靠传输(Reliable transmission)
通常在每个 TCP 报文段中都有一对
序号(sequence number,缩写:Seq)
和确认号(Acknowledgment number,缩写:Ack)
。TCP报文发送者称自己的字节流的编号为 Seq,称接收到对方的字节流编号为 Ack。有时候 TCP 报文的接收者为了确保可靠性,在接收到一定数量的连续字节流后才发送确认。这是对 TCP 的一种扩展,称为
选择确认(Selective Acknowledgement)
。选择确认机制使得 TCP 接收者可以对乱序到达的数据块进行确认。每一个字节传输过后,Seq 号都会递增 1。通过使用 Seq 和 Ack,TCP 层可以把收到的报文段中的字节按正确的顺序交付给应用层。Seq 是 32 位的无符号数,当它增长到 32 位的最大值(2^32-1)时,便会回绕为 0。
TCP 协议使用 Seq 标识每段发出的字节的顺序,从而接收者接受数据时可以重建顺序,防止传输时包发生乱序或丢包。在发送第一个握手包(SYN)时,TCP 协议会选择一个随机数作为 Seq 的初值,以防止
TCP 序号预测攻击
。 -
基于重复累计确认的重传(Dupack-based retransmission)
如果一个包(假设它的分段号是 100)丢失,接受者便不能确认这个包及以后的包。在接收方收到分段号 100 以后的另一个数据包时,会发送对第 99 分段包的确认。这种重复确认是包丢失的信号,发送方如果收到 3 次对同一个包的确认,就重传最后一个未被确认的包。
-
超时重传(Timeout-based retransmission)
发送方采用一个保守估计的时间作为收到数据包的确认的超时上限。如果超过这个上限仍未收到确认包,发送方将重传这个数据包。每当发送方收到确认包后,会重置
重传定时器
。如果重传定时器被触发,仍然没有收到确认包,定时器的值将被设为前次值的二倍(直到特定阈值)。这是为了防止一种通过欺骗发送者使其不断重传,进而压垮接受者的攻击,而使用重传定时器可以避免此类中间人攻击方式的拒绝服务攻击
。 -
错误检测(Error detection)
Seq 字段允许接收方丢弃重复的数据包,并对无序数据包进行正确排序。Ack 字段允许发送方决定何时重新传输丢失的数据包。
TCP 使用
校验和(checksum)
字段来确保报文的完整性和正确性。TCP 的 16 位校验和的计算和检验过程如下:发送者将 TCP 报文段的头部和数据部分的和计算出来,再对其求反码(一的补码),就得到了校验和,然后将结果装入报文中传输。(这里用反码和的原因是这种方法的循环进位使校验和可以在 16 位、32 位、64 位等情况下的计算结果再叠加后相同)接收者在收到报文后再按相同的算法计算一次校验和。这里使用的反码使得接收者不用再将校验和字段保存起来后清零,而可以直接将报文段连同校验加总。如果计算结果是全部为一,那么就表示了报文的完整性和正确性。
-
流量控制(Flow control)
流量控制用于避免主机分段发送得过快而使接收方来不及完全收下以至于丢包,一般由接收方通告发送给发送方进行调控。
TCP 使用
滑动窗口协议(Sliding Window Protocol)
实现流量控制。接收方在接收窗口(Window,缩写:Win)
字段中指出还可以接受的字节数量。发送方在没有接收到接收方新的确认包的情况下,至多发送接收方 Win 字段所指出的字节数量。当接收方宣布 Win 字段 的值为 0,发送方停止发送数据,并开始
保持定时器(Persist Timer)
,以避免因接收方在随后发送的窗口更新(Window Update)
丢失导致的死锁现象(发送方等待接收方窗口更新,接收方等待发送方传输数据)。当保持定时器到期时,TCP 发送方尝试恢复发送一个小的零窗口探针(Zero Window Probe,缩写:ZWP)
,希望接收方回应一个窗口更新包以更新窗口。当发送方应用进程发送大量很小的数据包、或接收方以很小的接受窗口处理到来的数据,那么每个 TCP 的数据包中只能发送很小的一些字节,这相对于 TCP 包头来说是很大的开销。极端情况下,有效载荷可能只有 1 个字节;而传输开销有 40 字节(20 字节的 IP 头部 + 20 字节的 TCP 头部),这被称作
愚钝窗口综合征(Silly Window Syndrome)
。有以下几种方法来避免愚钝窗口综合征:-
David D Clark 算法:接收端使用 David D Clark 算法,如果收到的数据导致 window size 小于某个值,可以直接 Ack 一个 win=0 的数据包,阻止发送端再发数据。等到接收端处理了一些数据后,window size 大于等于了 MSS,或者接收端 buffer 有一半为空,就可以把窗口打开让发送端再发数据过来。
-
Nagle 算法:发送端使用 Nagle 算法来延时处理:
- 如果包长度达到 MSS,则立即发送;
- 如果该数据包含有 FIN,则立即发送;
- 设置了 TCP_NODELAY 选项,则立即发送;
- 未设置 TCP_CORK 选项时,若所有发出去的小数据包(包长度小于 MSS)均被确认,则立即发送;
- 上述条件都未满足,则延迟发送(一般设定为 200ms)。
Nagle 算法的基本定义是任一时刻,最多只能有一个未被确认的小段。该算法的精妙之处在于它实现了
自时钟(self-clocking)
控制:ACK 返回得快,数据传输也越快。在相对高延迟的广域网中,更需要减少微型报的数目,该算法使得单位时间内发送的报文段数据更少。也就是说,RTT 控制着发包速率。- 延迟确认(Delay-Ack) 算法:
- 如果收到的数据内容达到 MSS,立即发送ACK;
- 如果有响应数据要发送,ACK 会随着响应数据一起并且立刻发送;
- 如果没有响应数据要发送,ACK 将会延迟一段时间,以等待响应数据;
- 如果在等待期间,对方的第二个数据报文到达,立刻发送 ACK;
-
-
拥塞控制(Congestion control)
拥塞控制(Congestion control)是发送方根据网络的承载情况控制分组的发送量,以获取高性能又能避免
拥塞崩溃(congestion collapse)
。这在网络流之间产生近似最大最小公平分配。发送方与接收方根据确认包或者包丢失的情况,以及定时器,估计网络拥塞情况,从而修改数据流的行为,这称为拥塞控制或网络拥塞避免。
TCP 的现代实现包含四种相互影响的拥塞控制算法:
慢启动(Slow start)
、拥塞避免(Congestion avoidance)
、快速重传(Fast retransmit)
、快速恢复(Fast Recovery)
。- 慢启动: TCP 在新建连接时为了防止网络拥塞,不会在一开始就大量发送数据包,而是根据网络情况逐步增加每次发送的数据量。在每个
往返时间(Round-Trip Time,缩写:RTT)
之后的此阶段,拥塞窗口(Congestion Window,缩写:cwnd)
的大小呈指数级增长:
# cwnd 初始化 Initially cwnd = 1 # 指数增长 After 1 RTT, cwnd = 2^(1) = 2 2 RTT, cwnd = 2^(2) = 4 3 RTT, cwnd = 2^(3) = 8
- 拥塞避免:当 cwnd 大于
慢启动阈值(slow start threshold,缩写:ssthresh)
时,进入拥塞避免阶段,在每个 RTT 之后加法增大
:
# cwnd 初始值 Initially cwnd = i # 加法增大 After 1 RTT, cwnd = i+1 2 RTT, cwnd = i+2 3 RTT, cwnd = i+3
-
快速重传:TCP 发送端在观测到至少 3 个
重复确认(Dup Ack)
后,即重传可能丢失的数据分组,而不需等待重传计时器超时。 -
快速恢复:如果发生拥塞,cwnd 大小将减小。发送方猜测发生拥塞的唯一方法是需要重新传输 TCP 分段。需要重新传输才能恢复假定由于拥塞而被路由器丢弃的丢失数据包:
# 如果由于超时而重新传输,会重新以慢启动阶段启动,且: ssthresh = ssthresh / 2 cwnd = 1 # 如果由于重复确认而重新传输,会重新从拥塞避免阶段开始,且: ssthresh = ssthresh / 2 cwnd = ssthresh ## 如果由于重复确认而重新传输,而且当前正在慢启动阶段,则: ssthresh = ssthresh / 2 cwnd = ssthresh + 3
- 慢启动: TCP 在新建连接时为了防止网络拥塞,不会在一开始就大量发送数据包,而是根据网络情况逐步增加每次发送的数据量。在每个
TCP 头部
- TCP 头部包含如下几个字段:
type TCP struct {
Source_port uint16
Destination_port uint16
Sequence_number uint32
Acknowledgment_number uint32
Flags uint16
Window uint16
CheckSum uint16
Urgent_pointer uint16
Options uint64
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具