# TCP 的首部
- 源端口:表示发送方的端口号
- 目的端口:表示接收方的端口号
- 序号:发送方一次 TCP 通信中每个字节的编号
- 确认号:对发送方发送的 TCP 报文段的确认
- 头部长度:表示 TCP 头部的长度,单位是 4 字节,最多为 15 个 4 字节 = 60 字节
- 窗口大小:TCP 流量控制的手段,表示缓冲区的大小。
- 校验和:校验数据的完整性。
- 紧急指针:紧急指针相对当前序号的偏移,也可以叫做紧急偏移。
# TCP 如何保证可靠的
- 三次握手、四次挥手确保连接的可靠性。
- TCP 是有状态的。会记录哪些数据发送、哪些被接受,保证数据包按序到达。
- TCP 是可控制的。有校验和、ACK 确认、重传机制、流量控制、拥塞控制。
# TCP 三次握手、四次挥手
略过~~
# 半连接队列和 SYN Flood 攻击的关系
- TCP 进入三次握手前,会在内部创建两个队列:半连接队列(SYN 队列)和全连接队列(ACCEPT 队列)。
- TCP 三次握手时,客户端发送 SYN 到服务端,服务端收到之后,便回复 ACK 和 SYN,状态由 LISTEN 变为 SYN_RCVD,此时这个连接就被推入了 SYN 队列,即半连接队列。
- 当客户端回复 ACK, 服务端接收后,三次握手就完成了。这时连接会等待被具体的应用取走,在被取走之前,它被推入 ACCEPT 队列,即全连接队列。
SYN Flood 是一种典型的 DoS 攻击,它伪造大量不存在的 IP 地址,向服务器发起 SYN 报文。当服务器回复 SYN+ACK 报文后,并不会收到 ACK 回应报文,导致服务器上建立大量的半连接导致其队列爆满。
主要有 SYN cookie 和 SYN Proxy 防火墙等方案应对。
SYN cookie:在收到 SYN 包后,服务器根据一定的方法,以数据包的信息为参数计算出一个 cookie 值作为自己的 SYN + ACK 包的序列号,回复后服务器并不立即分配资源,等收到发送方的 ACK 包后,重新根据数据包的源地址、端口计算该包中的确认序列号是否正确,如果正确则建立连接,否则丢弃该包。
SYN Proxy 防火墙:服务器建立一个防火墙,防火墙对收到的每一个 SYN 报文进行代理和回应,并保持半连接。等发送方将 ACK 包返回后,再重新构造 SYN 包发到服务器,建立真正的 TCP 连接。
上面两种做法的建立在,攻击者通常使用的是伪造的 IP 地址,无法发送 ACK 确认包。
# TCP 重传机制
# 超时重传
当发送方发送一个数据包后,会开启一个定时器,等待接收方的 ACK 确认。如果在 2 个 RTT 内没有收到 ACK 确认,就会重新发送这个数据包。
# 快速重传
跟超时重传的区别是,它以数据驱动而不是网络。
接收端遵循一个 TCP 原则:只接收连续的数据包。举个例子:
当发送端发送了 seq = 1、2、3 数据包,接收端收到了 1、2 数据包,它的 ACK 依次为 2、3。因为网络原因数据包 3 丢失了,即使发送端持续发送 seq = 4、5、6 数据包,接收端都只会返回 ACK =3。
基于这个特性,发送端如果发现 ACK 没有连续增长,就会认为网络出了问题,会重新发送丢失的数据包 3。
# 带选择确认的重传
上面的快速重传有一个缺点,发送端只能知道自己发送失败的最大有序号 3,如果数据包 seq = 4、5、6 实际发送成功了,它可以不用重传。所以 TCP 提供了 SACK 机制,接收端在首部标记收到了哪些范围的数据包。这样就可以做到只重传丢失的数据包。
# TCP 流量控制
TCP 流量控制是指接收方控制发送方发送数据的速率,避免接收方缓冲区溢出。
接收方会在 ACK 中携带自己的窗口大小,发送方会根据这个窗口大小来控制发送的数据量。
如果接收方的缓冲区满了,就会将窗口大小设置为 0,发送方就会停止发送数据。并且轮询接收方直到窗口大小不为 0,再继续发送数据。
这个滑动窗口的大小是同时受拥塞窗口(cwnd)和接收窗口(rwnd)的最小值决定的。
# TCP 拥塞控制
拥塞控制是基于网络情况的,控制发送方发送数据的速率,避免网络拥塞。
拥塞控制有四种算法:慢开始、拥塞避免、快重传、快恢复。
# 慢启动算法
发送方会将窗口大小设置为 1,然后每收到一次 ACK,拥塞窗口 cwnd 大小会翻倍(指数增长)。当到达 ssthresh 阈值或出现丢包时结束此阶段。
# 拥塞避免算法
当到达慢启动阈值时,转为线性增长,拥塞窗口 cwnd 大小每次增加 1 个 MSS。
# 拥塞发生
当网络拥塞而产生丢包时,会产生两种重传:
- 超时重传:会将 ssthresh 阈值设置为 cwnd/2,cwnd 大小设为 1,重新进入慢启动阶段,一夜回到解放前~~。
- 快速重传:会将 ssthresh 阈值设置为 cwnd/2,cwnd 大小设为 ssthresh,进入快速恢复阶段。
# 快速恢复算法
正如前面所说,进入快速恢复之前,cwnd 和 sshthresh 已被更新:
- cwnd = cwnd /2
- sshthresh = cwnd
然后,真正的快速算法如下:
- cwnd = sshthresh + 3
- 重传重复的那几个 ACK(即丢失的那几个数据包)
- 如果再收到重复的 ACK,那么 cwnd = cwnd +1
- 如果收到新数据的 ACK 后,cwnd = sshthresh。因为收到新数据的 ACK,表明恢复过程已经结束,可以再次进入了拥塞避免的算法了。
# TCP 的粘包和拆包
TCP 是面向字节流的协议,它不了解业务数据的含义。所以可能会将一个业务数据包拆分成多个 TCP 数据包发送,也可能将多个 TCP 数据包合并成一个业务数据包发送。
这就是 TCP 的粘包和拆包问题。解决方法如下:
- 定长协议:每个数据包的长度固定,接收方按照固定长度接收数据。
- 分隔符协议:每个数据包之间使用特殊的分隔符进行分割,接收方按照分隔符进行分割。
- 长度前缀:每个数据包都包含一个包头,包头中包含数据包的长度信息,接收方按照包头中的长度信息接收数据,类比 HTTP 的 content-length。
# Nagle 算法与延迟确认
如果发送方每次发送 1 字节的数据包,会导致可能会产生大量的网络流量,影响网络性能。
Nagle 算法就是为了解决这个问题,它会将多个小的数据包合并成一个大的数据包发送,从而减少网络流量,可以理解为延迟发送。
延迟确认是指发送方在发送数据包后,不会立即发送 ACK 确认,而是等待一段时间后再发送 ACK 确认。这样可以减少网络流量,提高网络性能。
参考文章:
TCP 协议经典十五连问 - 稀土掘金