目录
TCP是面向连接的传输层协议,目标是为用户提供可靠的端到端连接,保证信息有序无误的传输。它除了提供基本的数据传输功能外,还为保证可靠性采用了数据编号、校验和计算、数据确认等一系列措施。
它对传送的每个数据字节都进行编号,并请求接收方回传确认信息(ACK)。发送方如果在规定的时间内没有收到数据确认,就重传该数据。
6种错误:错 乱 延 少 无 重
(1) 数据编号使接收方能够处理数据的失序和重复问题。
(2) 数据误码问题通过在每个传输的数据段中增加校验和予以解决,接收方在接收到数据后检查校验和,若校验和有误,则丢弃该有误码的数据段,并要求发送方重传。
(3) 流量控制也是保证可靠性的一个重要措施,若无流控,可能会因接收缓冲区溢出而丢失大量数据,导致许多重传,造成网络拥塞恶性循环。
(4) TCP采用可变窗口进行流量控制,由接收方控制发送方发送的数据量。
TCP可靠传输机制
发送方需要缓存已发出但尚未收到 ACK 的包,接收方收到包但没有被用户进程消费之前也得把收到的包留着。但是,缓存是有大小限制的,程序消费数据和链路传输数据的能力也是有限的,发送端和接受端都需要滑动窗口和拥塞窗口机制来限制可发送或者可接收数据的最大范围。
1滑动窗口
滑动窗口分为:发送窗口swnd和接收窗口rwnd。由于TCP是全双工协议,发送端和接收端各自都有发送窗口和接收窗口。各自的接收窗口大小取决于应用、系统、硬件的限制(TCP传输速率不能大于应用的数据处理速率)。各自的发送窗口则要求取决于对端通告的接收窗口。
swnd = min(cwnd, rwnd),也就是拥塞窗⼝和接收窗⼝中的最⼩值。
拥塞窗⼝ cwnd 变化的规则:
只要⽹络中没有出现拥塞, cwnd 就会增⼤;
但⽹络中出现了拥塞, cwnd 就减少;
滑动窗口解决的是流量控制的的问题(接收端和发送端对数据包的处理速度不同)。接收端的缓存传输数据给应用层,这个过程不一定是即时的,如果发送速度太快,会出现接收端数据overflow,流量控制解决的是这个问题。TCP 首部里的 window 字段就是用来表示窗口大小:即接收方目前能接收的缓冲区的剩余大小。
发送方滑动窗口
已发送但还未收到ACK 和未发送 #2和#3这2部分都是发送窗口。
零窗口:如果发送方一直没有收到 ACK,随着数据不断被发送,很快可用窗口就会被耗尽。在这种情况下,发送方也就不会继续发送数据了,这种发送端可用窗口为零的情况称为零窗口。如果发送端陷入零窗口的状态,就会启动零窗口定时器,去定时地询问接收端窗口是否可用。
接收方滑动窗口
未收到但可接收的数据,#3部分属于接收窗口。如果进程读取缓冲区速度有所变化,接收端可能也会改变接收窗口的大小,每次通告给发送端,就可以控制发送端的发送速度了。这就是所谓的滑动窗口,也就是流量控制机制。由于TCP协议栈在操作系统内核中实现,应用程序无需直接设置窗口大小。
糊涂窗口综合症:窗口不断减少,发送的数据都比较小的了。就像大巴车只座1个人,浪费资源。TCP+IP头部=40字节。
nagle算法可解决这一问题,凑足一定数据再发送。
2 拥塞窗口
拥塞窗口 cwnd(congestion window)是发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。
1)滑动窗口解决的是发送方和接收方接收数据速率不一致的问题,通过设置滑动窗口(可以通俗的理解为接收方的缓存)可以缓解这一个问题。具体的操作是接收方会向发送方通知自己可以接受数据的大小,而发送方会根据这个数值,发送数据。
2)拥塞窗口用控制全局网络的拥塞情况。通过控制发送方每次发送的流量的多少,用来逐渐试探整体网络的拥塞程度。如果没有拥塞控制,发送方每次发送的数据大小为滑动窗口,在只有2台主机的时候确实是没有问题的,但是如果放到现实的网络大环境中来说是行不通的。因为如果每台主机都发送的窗口大小的数据,那么整个网络系统必然会瘫痪。所以通过在发送方设置拥塞窗口,可以有效缓解网络压力。
3 拥塞控制算法
慢启动、拥塞避免、快速重传、快速恢复
3.1慢启动算法
在 TCP 连接建立完毕后,会先使用慢启动算法,⼀开始初始化 cwnd = 1 ,表示可以传⼀个 MSS ⼤⼩的数据。之后指数级逐渐增大拥塞窗口(2 ,4 ,8…)
3.2拥塞避免算法
当拥塞窗口达到慢启动门限 ssthresh(slow start threshold)时,会使用拥塞避免算法,线性逐渐增大拥塞窗口。(+1 +1 +1…),⼀般来说 ssthresh 的⼤⼩是 65535 字节
当 cwnd < ssthresh 时,使⽤慢启动算法。
当 cwnd >= ssthresh 时,就会使⽤「拥塞避免算法」。
3.3超时重传算法
发生超时重传,将 ssthresh 设为 cwnd/2,将 cwnd 设为初始值,然后会再次使用慢启动算法。
超时重传图1
发送方每次只发送一个数据包,同时启动一个定时器,如果定时器超时依然没有收到该数据包的 ACK,则认为丢包并重传该数据包,如果收到 ACK,则重置定时器并发送下一个包;
3.4快速重传算法
发生快速重传,使用快速恢复算法,然后进入拥塞避免阶段。
当接收⽅发现丢了⼀个中间包的时候,发送三次前⼀个包的ACK,于是发送端就会快速地重传,不必等待超时再重传。
cwnd = cwnd/2 ,也就是设置为原来的⼀半;
ssthresh = cwnd ;
快速重传和快速恢复图2
快速重传机制只解决了一个问题,就是超时时间的问题,但面临着另外一个问题:是重传丢失的包一个,还是重传丢失之后的所有包。比如1,3,4,5发送成功了,是重传 Seq2 呢?还是重传 Seq2、Seq3、Seq4、Seq5 呢?因为发送端并不清楚这连续的三个 Ack 2 是谁传回来的。
SACK可以解决这个问题
3.5快速恢复算法
快速重传和快速恢复算法⼀般同时使⽤,快速恢复算法是认为,还能收到 3 个 ACK 说明⽹络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。进⼊快速恢复之前, cwnd 和 ssthresh 已被更新了:cwnd = cwnd/2 ,也就是设置为原来的⼀半;ssthresh = cwnd ;
快速恢复算法如下:
拥塞窗口 cwnd = ssthresh + 3 ( 确认有 3 个数据包被收到了);
重传丢失的数据包;
如果再收到重复的 ACK,那么 cwnd 增加 1;
如果收到新数据的 ACK 后,把 cwnd 设置为第⼀步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进⼊拥塞避免状态;
3.6 SACK
SACK( Selective Acknowledgment 选择性确认)。
这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将缓存的地图发送给发送方,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。
SACK示意图
如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)。
D-SACK示意图----ACK丢包
D-SACK示意图----网络延时
Linux 下可以通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能(Linux 2.4 后默认打开)。
3.7 NACK
接收方定时把所有未收到的包序号通过反馈报文通知到发送方进行重传
例如音视频数据包发送方按顺序发送了时间戳为60,120,180,240,320共五个包,接收者已经收到了60包,本来预期下一个接收的包的序号应该是序号120的包,但序号是120的包一直没收到,后面的包却收到了。那么接收者就可以判断,120这个包丢了,这时候接收方需要向发送方发出NACK消息(消息中带有丢包的序号,这里是120),让发送方重新发送丢失的包。
当发送方未在规定时间内接收到 ACK 确认包时,就会超时重传。每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。
3.8重传超时的计算规则
RTO (重传超时时间,Retransmission Timeout) 是发送端用来判断数据包丢失和执行重传的参数,该值应该是一个随网络传输的 RTT (往返时延) 而变化的值,理想情况下,RTO 的值>= RTT ,实际情况下,RTT 变化非常频繁,每一次传输的 RTT 可能都不一样。
TCP 协议采用的 RTO 重传超时时间计算方法:
基于多次 RTT 测量,给出一个平滑后的 RTT 预估值,即 SRTT (Smoothed Round Trip Time)
RTO = SRTT + 某种系数 (防止抖动的阈值)
系统级别设置 RTO 的下限为 100ms 或 200ms,防止异常值
对于重传包的 RTO,加上退避算法,比如,每重传一次,则 RTO = 2 * RTO 以减少对一个包频繁的
发送缓冲区
接收缓冲区
4 粘包问题
保护消息边界,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息。也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。UDP协议有消息边界,不存在粘包问题。
而TCP面向流则是指无保护消息保护边界的,存在粘包问题:
1 )发送端引起的粘包
发送端需要等缓冲区满才发送出去,造成粘包
nagle算法就是为解决网络中小包太多 合并大包来提升网络传输效率的算法
开启 TCP_NODELAY,禁用nagle算法
nagle算法:如果包的大小满足MSS,那么可以立即发送,否则数据会被放到缓冲区,等到已经发送的包被确认了之后才能继续发送。
通过这样的规定,可以降低网络里小包的数量,从而提升网络性能。
如果开启了这个算法 (默认),则协议栈会累积数据直到以下两个条件之一满足的时候才真正发送出 去:
- 积累的数据量到达最大的 TCP Segment Size == 即MSS
- 收到了一个 Ack
TCP-MSS(TCP Maximum Segment Size,TCP最大报文段长度)是指TCP连接的对端发往本端的最大TCP报文段的长度。当一个连接建立时,连接的双方都要通告各自的MSS(因此MSS选项只能出现在SYN报文段中)。对于IPv4,为了避免IP分片,主机一般默认MSS为536字节 (576IP最大字节数-20字节TCP协议头-20字节IP协议头=536字节)。同理,IPv6的主机默认MSS为1220字节(1280IP最大字节数-20字节TCP协议头-40字节IP协议头=1220字节)
延伸概念MTU:
MTU(Maximum Transmission Unit,最大传输单元)是指链路层的最大传输单元,包括TCP头和IP头,不包括链路层封装头。
TCP Delayed Acknoledgement 类似,它的作用就 是延迟 Ack 包的发送,使得协议栈有机会合并多个 Ack,提高网络性能。 Delayed Ack 默认超时时间 40ms,
现代的 TCP/IP 协议栈实现,默认几乎都启用了nagle和TCP delayed ack这两个功能
2 )接收方引起的粘包
接收方不及时接收缓冲区的包,造成多个包接收
3 )解决方案
设计好数据包格式,包头,包尾分隔符,包类型,包大小,校验信息;
接收端开启预处理线程拆包
5 wireshark实测tcp协议
结合上图我们使用最多的DIX Ethernet V2以太帧格式(另一种是IEEE的802.3标准),tcpdump抓包样例如下:
wireshark查看该tcpdump数据包,共分为4层,从上到下依次物理层(以太网mac帧),数据链路层(mac地址) ,网络层(ip地址),TCP传输层(端口)和TLS传输层(加密)
物理以太帧参数意义:
Frame 33: 393 bytes on wire (3144 bits), 393 bytes captured (3144 bits)33号帧,实际捕获393字节
Arrival Time: Mar 3, 2023 18:00:33.119845000 CST 捕获日期和时间
[Time delta from previous captured frame: 0.076516000 seconds] 此包与前一包时间间隔
[Time since reference or first frame: 1.276950000 seconds] 此包与第一个帧的时间间隔
Frame Number: 33 帧序号
Frame Length: 393 bytes (3144 bits) 帧长度
Capture Length: 393 bytes (3144 bits) 捕获帧长度
[Frame is marked: False] 此帧是否做了标记:否
[Protocols in frame: eth:ethertype:ip:tcp:ftp] 帧内封装的协议层次结构
[Coloring Rule Name: TCP] 用不同颜色的染色标记的协议名称:TCP
[Coloring Rule String: tcp] 染色显示规则字符串
tcp传输层参数意义:
SYN表示建立连接
ACK表示响应
PSH表示接收端收到数据后,是否立即传给上层
RST表示连接重置
FIN表示关闭连接
ack序号=当次对端seq序号+len
seq序号=上次本端seq序号+len
以下是wireshark添加的标记,并非TCP报文内容
发送端触发
[TCP window Full]作为发送方的标识,当前发送包的大小已经超过了接收端窗口大小,wireshark会打上此标识,标识不能在发送。
[TCP Fast Retransmission]快速重传,一般快速重传算法在收到三次冗余的Ack,即三次[TCP dup ack XXX#X]后,发送端进行快速重传。
接收端触发
[TCP window update] 当接收端接收窗口大小发生变化,可以接收数据了,则有该标志。
[TCP ZeroWindow]作为接收方发出现的标志,表示接收缓冲区已经满了,此时发送方不能再发送数据,一般会做流控调整。接收窗口,也就是接收缓冲区win=xxx ,告诉对方接收窗口大小。
[TCP dup ack XXX#X]表示第几次重新请求某一个包,#前XXX表示第几个包(不是Seq),#后的X表示第几次请求。丢包或者乱序的情况下,会出现该标志。
[TCP Retransmission]超时重传,如果一个包的丢了,又没有后续包可以在接收方触发[Dup Ack],或者[Dup Ack]也丢失的情况下,TCP会触发超时重传机制。
[TCP Previous segment not captured],丢包。
[TCP Out-Of-Order]指的是TCP发送端传输过程中报文乱序