5.3 TCP 协议

5.3.1 TCP 协议的特点
TCP 是面向连接的传输层协议。
5.3.2 TCP 报文段

(1)源端口和目的端口
各占 2B。运输层的分用、复用都是通过端口实现。
(2)序号
占 4B,TCP 传送是面向字节流的(即 TCP 传送是逐个字节传送的),所以序号字段值指的是本报文段所发送数据的第一个字节的序号。
(3)确认号 占 4B。
(4)数据偏移(首部长度)
占 4 位。TCP 数据部分起始处在哪,单位是 4B,所以 TCP 首部长度为 20~60B。
(9)复位位(RST)
当 RST=1 时,表明 TCP 连接中出现严重差错(如主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。
(11)终止位 FIN
当 FIN=1 时,表明此报文段的发送方的数据已发送完毕,并要求释放运输连接。
(12)窗口
占 2B。允许对方发送的数据量。
(13)校验和
占 2B。发送方必须发送,接收方必须检查。
(15)选项
MSS(最大报文段长度)。MSS 是 TCP 报文段中的数据字段的最大长度。
TCP 选项至少支持以下选项。
00000000:选项结束
00000001:无操作
00000010|00000100|MSS:只在 SYN 中使用,其他字段不能使用。
种类 | 长度 | 意思 |
---|---|---|
0 | - | End of Option List Option |
1 | - | No-Operation |
2 | 4 | 最大报文段长度(MSS) |
如果设置时没有收到 MSS 选项,IPv4 的 MSS 默认为 536,IPv6 的 MSS 默认为 1220

TCP 首部最短长度为 20B,而最大长度为 60B,因为数据偏移为 4 位,$(2^4-1) \times 4=60B$。
确认位 ACK=1 表示正确收到
确认号 N:到序号 N-1 为止的数据都已经正确收到,期望收到的第一个字节的序号 N
序号:本报文段发送的第一个字节的序号
同步位 SYN
SYN=1,ACK=0,连接请求报文
SYN=1,ACK=1,连接接受报文
TCP 以段为单位发送数据
三次握手将在 SYN 包中确认 MSS 数据。

MSS 设置多大合适?
MSS 默认值是 536B。
5.3.3 TCP 连接管理
TCP 中发送第一个 SYN 包的一方叫客户,接收这个的一方叫服务器。
建立一个 TCP 连接需要发送 3 个包。这个过程也称作“三次握手”。
一个连接的建立与断开,正常过程至少需要来回发送 7 个包才能完成。
在建立 TCP 连接时,也可以确认发送数据包的单位。
TCP 实现应该在每个 SYN 段中发送 MSS 选项,并且可以始终发送 TCP 是端到端通信
TCP 连接的端点是套接字
一条连接使用它们的套接字表示 (1,x)-(2,y) 是两个端口唯一的连接,后建立的连接会被阻止。
1.TCP 连接建立(三次握手)

SYN=1,seq=x(随机),第一个 SYN 报文段不能携带数据,但要消耗一个序号
SYN=1,ACK=1,seq=y(随机),ack=x+1,确认报文段,不能携带数据,但要消耗掉一个序号。
ACK=1,seq=x+1,ack=y+1,该报文段可以携带数据,若不携带数据则不消耗序号。
客户机:我能和你说句话吗?
服务器:可以
客户机:好的
第 3 次握手可以携带数据,若不携带数据则不消耗序号。
SYN 洪泛攻击:
一直发送 SYN,不回复,导致服务器一直发送 ACK,占用资源直到服务器死机。
为什么不采用“两次握手”建立连接?
为了防止两次握手情况下已失效的连接请求报文突然又传送到服务器而产生错误。

由上图可以看出,两次握手情况下已失效的连接请求报文突然又传送到服务器,导致服务器以为有新的连接请求,进而白白等待,消耗资源。
2.TCP 连接释放(四次握手)

FIN=1,seq=u,FIN 报文段及时不携带数据,也要消耗掉一个序号
ACK=1,seq=v,ack=u+1(半关闭状态,客户到服务器方向的连接释放)
FIN=1,ACK=1,seq=w,ack=u+1(FIN 表示单方面释放连接,表示本方已经无数据发送,但可以接收对方数据)
ACK=1,seq=u+1,ack=w+1
A(客户机):我说完了
B(服务器):好的,我想说。。。。
B:我说完了
A:好的
A 说完话后,TCP 状态变为 TIME_WAIT,此时 TCP 连接还没有释放,还要等 2MSL(2 个最长报文段寿命),客户机才进入 CLOSED(连接关闭)状态。
为什么不采用“三次握手”释放连接?
为了防止三次握手情况下已失效的连接请求报文突然又传送到服务器而产生错误。
为什么发送最后一次握手报文后要等待 2MSL 时间?
为了防止最后的确认报文段能够到达 B。如果最后的确认报文段未到达 B,而且 A 关闭的话,B 就无法关闭。

没有 2MSL 的话,新进程可能会收到就进程的 TCP 报文段,从而产生错误。
参考资料:RFC793
5.3.4 TCP 可靠传输
1.序号
2.确认
3.重传
(1)超时重传
(2)冗余 ACK
4.校验
5.3.5 TCP 流量控制
流量控制是什么?发送方不会淹没接收方。

接受窗口:接收方根据自己接收缓存的大小,动态地调整发送方的发送窗口大小。
TCP 中滑动窗口的值设置太大,对主机的影响是?
如果滑动窗口的值设置得太小,那么会产生过多的 ACK(因为窗口大可以累积确认,因此会有更少的 ACK)
5.3.6 TCP 拥塞控制(发送方如何维护拥塞窗口)
拥塞控制是什么?发送方不会淹没接收方。
拥塞控制包含四种算法:慢开始、拥塞避免、快重传、快恢复。
拥塞窗口 cwnd:发送方根据自己估算的网络拥塞程度而设置的窗口值。
发送窗口上限:min[接受窗口,拥塞窗口]
1.慢开始
拥塞窗口=1,2,4,8,……
发送方开始只发送一个报文段,目的是试探一下网络的拥塞情况。
初始的拥塞窗口为最大报文段长度 1KB。

2.拥塞避免
拥塞窗口+=1
慢开始门限 ssthresh 是前一次拥塞的一半
- cwnd<ssthresh:慢开始
- cwnd = ssthresh,发送方可以使用慢启动或拥塞避免 (通常做法)。
- cwnd>ssthresh:拥塞避免
网络拥塞后:ssthresh=cwnd/2,cwnd=1
(3)网络拥塞的处理
无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(未按时收到确认),就要把慢开始门限设置为出现拥塞时的发送方的 cwnd 值的一半(但不能小于 2)。然后把拥塞窗口 cwnd 重新设为 1,执行慢开始算法。这样做的目的是迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够的时间吧队列中积压的分组处理完。
注意⚠️:慢开始不能越过 ssthresh
3.快重传
发送方收到三个连续的 ACK 报文后,直接重传对方未收到的报文段。减少重传时间。

4.快恢复
当发送方连续收到三个冗余 ACK(重复确认)时,执行“乘法减小”算法,把慢开始门限设置为此事发送方 cwnd 的一半。因为发送方现在认为网络很可能没有发生严重拥塞,否则就不会有几个报文段连续到达接收方,也不会连续收到重复确认。
网络拥塞后:ssthresh=cwnd/2,cwnd=ssthresh,然后拥塞避免
就是跳过了慢开始
TCP 连接建立和网络出现超时时:慢开始和拥塞避免
发送方收到冗余 ACK 时,采用快重传和快恢复
如果所有链路传输都不出现差错,所有的结点也不会发生故障,TCP 的“可靠交付”功能是否就是多余的?
不是多余的。这时为了防止出现以下情况。
(1)每个 IP 数据报独立地选择路由,因此在到达目的主机时有可能出现失序。
(2)由于路由选择的就散出现错误,导致 IP 数据报在互联网中转圈。最后数据报首部中的生存空间(TTL)的数值下降到零。这个数据报在中途就被丢弃
(3)某个路由器突然出现很大的通信量,以至路由器来不及处理到达的数据报。因此有的数据报被丢弃。
cwnd(拥塞窗口) | ssthresh(慢开始门限) | 触发条件 | 丢包超时 | |
---|---|---|---|---|
慢开始 | 1、2、4、8...... | 不变 | TCP 连接建立且 cwnd<ssthresh | ssthresh=cwnd/2 |
拥塞避免 | x、x+1、x+2 | 不变 | cwnd>ssthresh | ssthresh=cwnd/2,执行慢开始 |
快重传 | cwnd/2 | 当发送方连续收到三个冗余 ACK | ||
快恢复 | cwnd/2 | 当发送方连续收到三个冗余 ACK |
为什么超时事件发生时 cwnd 被置为 1,而收到三个冗余 ACK 时 cwnd 减半?
因为收到三个冗余 ACK,网络虽然拥堵,但至少还有 ACK 交付。而当超时事件发生时,说明网络可能拥塞得连 ACK 报文段都传输不了,发送方只能等待超时后重传。所以超时事件发生后,应该最大限度限制发送方数据发送量,cwnd 置为 1。收到三个冗余 ACK 时,发送方稍微抑制一下发送量,所以 cwnd 减半。
TCPIP 序号到底是什么?
序号是 TCP 报文段数据部分的字节的编号,一个字节占用一个序号。
参考资料:RFC5681