1. TCP包头
关注重点:源端口号,目的端口号,序号,确认序号,状态位(ACK回复, RST重新连接, SYN发起一个连接, FIN结束连接)
2. TCP关注的5大问题
- 顺序问题,稳重不乱
- 丢包问题,承诺靠谱
- 连接维护,有始有终
- 流量控制,把握分寸
- 拥塞控制,知进知退
注意:流量控制是针对另一端的,拥塞控制是针对网络的
3. TCP三次握手
两次握手行不行?
答:两次握手对应A发了一次,B收到了再发一次(B知道A希望建立连接)。B不能确认A是否能收到它的应答(应答包会丢失,会变慢,甚至A挂了);还有一个问题是A之前为了建立连接可能发送了多次请求,在第一次连接结束后可能这些请求到达了B,B以为要重新建立连接,于是发送应答然后就等待A,这样是错误的,浪费B的资源
4次握手行不行?
答:也是可以的,但是4次,40次,400次也不能绝对保证可靠,只要双方消息有去有回,就基本可以了。
为什么要有序号?
答:序号是为了避免不同的任务相互冲突(序号是基于时间的32位计数器,没4微妙加1,可以有效避免冲突)
4. TCP四次挥手
MSL:Maximum Segment Lifetime,报文最大生存时间,超过这个时间的报文将被丢弃,可以设置
FIN_WAIT_2时间怎么设置?
TCP没有,但是Linux可以调整tcp_fin_timeout来设置。
如何B超过2MSL还没有接收到A的FIN的ACK会怎么样?
答:B会重新发送FIN,A受到包,发现已经等待超过2MSL了,之前的连接已经断了,于是发送RST,重新建立连接
5.TCP状态机
加黑加粗的部分,是上面说到的主要流程,其中阿拉伯数字的序号,是连接过程中的顺序,而大写中文数字的序号,是连接断开过程中的顺序。加粗的实线是客户端 A 的状态变迁,加粗的虚线是服务端 B 的状态变迁。
6. 顺序问题、丢包问题、流量控制——滑动窗口(rwnd)
累计确认或者累计应答:TCP协议不是说受到一个确认一个,这样太慢了,而是应答一个ID,表示之前的都收到了。
发送端:
图中从左到右分别对应4个部分
接收端:
6.1 顺序问题和丢包问题
超时重传:也即对每一个发送了,但是没有 ACK 的包,都有设一个定时器,超过了一定的时间,就重新尝试,时间必须大于往返时间RTT,但是具体多少可以通过自适应重传算法控制。
超时间间隔加倍:每当遇到一次超时重传,TCP都会将下一次的时间间隔设置为之前的2倍,这样的问题是超时周期可能过长,解决的办法有:
- 快速重传机制,接收方接收到序号大于期望报文段时发送冗余的ACK,然而ACK是期望接收到的报文段,客户端接收到冗余ACK后会在定时器过期之前发送丢失的报文段
- 还有一种方式是SACK(Selective Acknowledgment),这种方式需要在TCP头里加入SACK,可以将缓存的地图发送给发送方,发送方根据这个地图就知道哪个包丢了,于是重传
6.2 流量控制问题
通过调整窗口大小来控制
7. 拥塞控制——拥塞窗口(cwnd)
TCP拥塞控制主要用来解决两个问题:包丢失和超时重传
慢启动:TCP刚开始时将cwnd设置为1,然后顺利的话将cwnd翻倍(指数增长),当cwnd达到阈值ssthresh时改为线性增长,然后当出现丢包时,需要超时重传,就将sshresh设置为cwnd/2,将cwnd设置为1,但是这种方式比较激进,会造成网络卡顿。可以通过快速重传算法来解决(cwnd设置为csnd/2,sshthresh设置为cwnd)
TCP拥塞控制存在的问题:
- 丢包不一定代表网络带宽被填满了
- TCP拥塞控制等待中间设备被填满了(包括缓存)才降低速度,这时候已经晚了
优化这两个问题可以通过TCP BBR拥塞算法解决:找到一个平衡点,将中间管道填满,但是不填满缓存