TCP的设计理念是网络世界是一个环境很差的世界,丢包,乱序,重传,拥塞是常有的事情。
从报文来说明TCP链接
1.端口号是必须要的,如果没有端口号,则内核不知道这个包是投递给哪个应用程序来处理
2.序号, 解决的事网络传包的乱序问题,没有编号就不知道哪个应该先来,哪个后来。
3.确认序号,解决的事网络丢包问题,应用端收到了这个网络包,确认后才代表真的已经收到,算法层保证的靠谱协议,如果没有收到确认,那就重传
4.标记位 SYN是发起,ACK是回应,RST是重连,FIN是结束。TCP是面向链接的协议,这些带有状态位的包就会改变TCP链接状态
5.窗口大小 TCP需要做流量控制,双方需要根据自己的处理能力,协商窗口大小,当网络不好的时候,你无力影响外部网络,只能控制自己少发包。
TCP的三次握手
为什么是三次握手,这是一个请求之应答机制,A发送包给B;B收到A的包,并告诉A我收到了,但是B的包一入网络深似海,B也要确认刚才回给A的包,A有没有收到;A需要再给B回复一下我收到你的应答了。
三次握手的另一个主要目的是沟通序号问题,A发起的包给B需要告诉B我这边发起的包是从什么序号开始。B同样需要告诉A,由B发起的数据包的起始序号是多少。
SYN (synchronize sequence number)同步序列编号。
TCP的四次挥手
TCP状态机
累计应答策略:TCP协议为了保证顺序性,每个数据包都有一个ID,发送方按照次序依次发送数据包,为了保证包不丢失,接收方需要对每个数据包进行应答,告诉发送方已经收到。
为了记录这些发送和接受的包,发送端和接受端也需要缓存这些记录。
发送方记录:
1.已经发送并已经确认
2.已经发送但是还没有确认
3.没有发送,但是已经准备发送的
4.还没有发送,并且暂时也不能发送的
TCP会给发送端报一个窗口大小,称advertising window这个大小应该等于已经发送未确认大小加上还没发送准备发送的大小,超过了这个部分,就是不可发送了,发的太多会导致接受端忙不过来。
上图中有三个分界线,中间黑框是一个advertising windows大小。
接收方记录:
1.接受并已经确认
2.还没接受,但是马上可以接受的
3.还没接受,但是也不能接受的,是已经超过工作量的部分
MaxRcvBuffer:最大缓存量
LastByteRead:已经接受了,但是还没有被应用读走的部分
第二部分收到的包可能不是顺序的,可能会存在空档,只有和第一部分连续的才可以马上回复,中间空着的需要等待。
顺序问题和丢包问题
确认和重发机制
超时重试:每次发送一个数据包,都会起一个定时器,超过一定时间没有收到ack就会重新发送,这个定时器设置的时间必须大于RTT往返时间,估计往返时间是TCP采样加权的往返时间。由于这个往返时间是不断变化的,这个称为自适应重传算法
如果重传后还是没有收到ack,这时TCP的策略是超时时间加倍。
如果每次都是等发送端重新发送,这样会导致等待的时间过长,这里有两种解决途径来缩短等待时间。
1.当接收方收到一个序号比期望序号大的数据包时,会发送三个冗余ack,客户端收到就会在定时器超时之前重传这段报文
2.selective acknowledgement,需要在TCP的头里面加上sack,将缓存的地图发给发送方,发送方一下子就可以看出来有个空格没有接受到
流量控制问题:
发送端和接收方会维护一个窗口大小的机制,窗口大小是接受端的最大缓存量减除已经接受的包但是应用层还没有读取的大小。
接收方每次回复发送方的ack时,会同时携带窗口大小的,发送方根据这个窗口大小,把维护的窗口进度条右移。
如果接受端的应用层一直不读走缓层的消息就会导致窗口大小一直为0,这样的话,发送方会定期发送窗口探测数据包,看是否有机会调整窗口大小。为防止接收方空出一个位置就告诉发送方导致的低能窗口综合症,有少量变化,并不马上更新窗口,而是当缓冲区一半为空时,才更新窗口
拥塞控制问题:
上面讨论的是rwnd(滑动窗口)是怕把接受方撑满
而cwnd时怕把网络撑爆。
LastByteSend-LastByteAcked=min(rwnd, cwnd);拥塞窗口和滑动窗口共同决定发送速度。
***BBR
充分利用资源,并尽可能不去填满缓存,因为填满缓存会导致延迟增大。