TCP 的特点
- 面向连接,TCP 连接是一条逻辑连接
- 只能一对一传输
- 提供可靠交付服务
- 全双工通信
- 面向字节流,将应用层交付的一次一个数据块仅看作一连串的无结构字节流
TCP 报文段
字段 | 长度 | 描述 |
---|---|---|
源端口和目的端口 | 各 2B | 发送方和接收方的端口 |
序号 | 4B | 本报文段发送的第一个字节的序号 |
确认号 | 4B | 期望收到对方下一个报文段的第一个字节的序号 |
数据偏移 | 4bit | 表示 TCP 报文的数据起始处距离报文起始处距离多远,也就是表示首部长度,单位是 4B |
保留 | 6bit | 置为 0,保留到今后使用 |
紧急位 URG | 1bit | URG=1 则表明该报文段中有紧急数据,紧急数据在数据部分的最前面,根据紧急指针字段判断紧急数据有多少字节 |
确认位 ACK | 1bit | ACK=1 时,确认号字段才有效。TCP 连接建立后,ACK 应该一直置一 |
推送位 PSH | 1bit | 接收方 TCP 收到 PSH=1 的报文后,尽快交付接收应用进程,而不再等到缓存填满后才向上交付 |
复位位 RST | 1bit | RST=1 表示 TCP 连接出现严重差错,需要释放连接,重新建立连接。也可以用于拒绝一个非法报文段 |
同步位 SYN | 1bit | SYN=1 表示这是一个连接请求或连接接收报文 |
终止位 FIN | 1bit | FIN=1 表示发送方的数据已发送完毕,请求释放连接 |
窗口 | 2B | 剩余接收窗口大小,单位为 B |
检验和 | 2B | 和 UDP 一样,要在 TCP 前面加上伪首部,再计算整个报文的检验和 |
紧急指针 | 2B | 仅在 URG=1 时才有效,表示数据部分开头紧急数据的字节数 |
选项 | 0~40B | 额外选项,如最大报文段长度 MSS 表示数据部分的最大长度(不包括首部) |
填充 | 填充使整个首部长度为 4B 的整数倍 |
TCP 连接管理
连接建立
第一次“握手”:客户端主动发送 SYN=1 的连接请求报文段,并确定一个初始序号 seq=x,SYN 报文段不携带数据,但要消耗一个序号。
第二次“握手”:服务器收到后返回 ACK=1 表示确认收到请求,SYN=1 表示这是连接请求报文段,确认一个初始序号 seq=y,ack=x+1 表示客户端可以开始发送下一个报文段。
第三次“握手”:客户端收到后返回 ACK=1 表示确认收到回应,可以开始从序号 x+1 开始发送报文段,并期望收到服务端的下一个报文段 y+1,也可以不发送数据。
Tip
第三次握手时就可以开始发送数据。
连接释放
第一次“挥手”:打算关闭连接时,客户端主动发送 FIN=1 的释放连接报文,seq 为上一次发送数据的最后一个字节的序号+1 ,FIN 报文段虽然不携带数据,但是也要消耗一个序号。
第二次“挥手”:服务端收到后,回复 ACK=1 表示收到,ack 表示收到客户端的数据,seq 为服务端上一次发送数据的最后一个字节的序号+1 。
Tip
此时,从客户端到服务端的连接已经单向关闭,但是 TCP 连接是全双工连接,所以服务端在发完要发的数据后也要主动关闭连接。
第三次“挥手”:服务端发完要发的数据后,主动发送 FIN=1 的释放连接报文段,ACK=1,seq 为刚刚发完的最后一个序号+1,ack 希望收到客户端的下一个序号的报文段。
第四次“挥手”:客户端收到后,ACK=1,发送下一个 seq 序号,并期望收到服务端下一个序号的报文。客户端发送该报文之后,进入 TIME-WAIT 状态,等待 2 倍的 **MSL(最长报文段寿命)**之后,关闭连接。而服务器端在收到该报文后直接关闭连接。
Tip
保活计时器:避免服务器在客户机突然故障时一直等待。 时间等待计时器:在 TIME-WAIT 阶段使用,其值设置为最长报文段寿命的两倍 2MSL。
TCP 可靠传输
TCP 连接传送的数据流中的每个字节都有一个序号,TCP 报文的序号字段的值是该报文发送的数据的第一个字节的序号。
TCP 默认使用累计确认,即收到确认号为 u,则表示序号在 u 之前的字节已经全部正确到达。
冗余 ACK:TCP 规定每当比期望序号大的失序报文段到达时,就发送一个冗余 ACK,指明下一个期待字节的序号。当发送方连续收到 3 个期望同一序号的冗余 ACK 时,就直接认为在期待的字节后面的这个字节数据已经丢失。
只有失序到达才返回冗余 ACK,首次收到的请求序号的报文不计入冗余 ACK。
超时和冗余 ACK会导致 TCP 对报文段进行重传。
Tip
使用重传计时器进行计时。
TCP 流量控制
和数据链路层的流量控制相似,传输层 TCP 的流量控制也使用滑动窗口机制
传输层和数据链路层的流量控制的区别:
范围 | 接收窗口大小 | |
---|---|---|
传输层 | 端到端 | 可以动态变化 |
数据链路层 | 点到点 | 固定 |
三种确认机制的对比:
协议 | 确认机制 | 对失序报文的处理 |
---|---|---|
TCP | 累积确认 | 缓存起来,并发送冗余 ACK |
GBN | 累积确认 | 直接丢弃 |
选择重传 | 每一帧单独确认 | 缓存起来 |
Tip
TCP 流量控制相当于 GBN 和选择重传 SR 的结合。
零窗口持续计时器
为了打破非零窗口通知报文段丢失而引起的双方,互相等待的死锁局面,TCP 为每个连接都设有一个持续计时器。
- TCP 的一方收到零窗口通知后启动持续计时器
- 计时器超时时,发送一个零窗口探测报文段,仅携带 1 字节数据
- 对方在确认时,给出最新的接收窗口值
- 如果最新的 rwnd 还是 0,则重新进行这个流程
Tip
相当于发一个携带 1 字节数据的报文来试探,以此重新获得对方的 ACK,其中包含了窗口值。
TCP 拥塞控制
TCP 还要求发送方维护一个拥塞窗口(cwnd),大小取决于网络的拥塞程度,并且动态变化。
网络不拥塞时,不需要拥塞控制,所以只需要和 TCP 流量控制一样,接收窗口 rwnd 是多少发送窗口 swnd 就是多少。网络拥塞的时候就要考虑拥塞窗口 cwnd
拥塞控制的算法有四种:慢开始、拥塞避免、快重传和快恢复。这些算法都是根据网络拥塞情况动态调整拥塞窗口 cwnd 的算法。
设置一个慢开始门限 ssthresh,在 ssthresh 之前使用慢开始,在 cwnd 大于 ssthresh 时使用拥塞避免。发生超时后 ssthresh=当前 cwnd/2
- 慢开始:cwnd 从 1 开始,每经过一个 RTT 乘 2,指数级增长
- 拥塞避免:每经过一个 RTT,cwnd+1
- 快重传:发送方一旦连续收到 3 个冗余 ACK(即重复确认),就立即重传相应的报文段,而不是等待超时才重传。
- 快恢复:将 cwnd 和 ssthresh 都设置为当前 cwnd 的一半,然后直接开始拥塞避免算法
总结:TCP 连接刚建立和网络出现超时时,使用慢开始和拥塞避免算法;当发送方收到 3 个冗余 ACK 时,采用快重传和快恢复算法。
慢开始的 cwnd 增大机制
慢开始在每收到一个对新的报文段的确认后,把拥塞窗口增加之多一个 MSS 的数值,用这样的方法逐步增大发送方的拥塞窗口 cwnd,可以使分组注入到网络的速率更加合理。体现在时间上就是每经过一个传输轮次 RTT,cwnd 乘 2 。
TCP 的发送窗口
经过流量控制和拥塞控制的双重限制,TCP 最终的发送窗口的上限值=min{rwnd, cwnd}