TCP
Posted luozhiyun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP相关的知识,希望对你有一定的参考价值。
TCP包头格式
首先,源端口号和目标端口号是不可少的.
接下来是包的序列号.
然后就是确认序号.
接下来就是状态位.例如SYN是发起一个连接,ACK是回复,RST是重新连接,FIN是结束连接
还有一个重要的是窗口大小.TCP要做流量控制,通信双方各声明一个窗口,标识自己当前能够的处理能力.
TCP 的三次握手
一开始,客户端和服务端都处于CLOSED状态.先是服务端主动监听某个端口,处于LISTEN状态.然后客户端主动发起连接SYN,之后处于SYN-SENT状态.服务端收到发起的连接,返回SYN,并且ACK客户端的SYN,之后处于SYN-RCVD状态.客户端收到服务端发送的SYN和ACK之后,发送ACK的ACK,之后处于ESTABLISHED状态,因为它一发一收成功了.服务端收到ACK的ACK之后,处于ESTABLISHED状态,因为它也一发一收了.
TCP的四次挥手
断开的时候,我们可以看到A发了一个断开的数据包给B,然后就进入了FIN_WAIT_1的状态,B收到后就发送一个回应,就进入了CLOSE_WAIT的状态
A收到了B的数据包之后就进入了FIN_WAIT_2的状态,如果这个时候B直接断开,则A将永远在这个状态.TCP协议里面并没有对这个状态的处理,但是Linux有,可以调整TCP_FIN_TIMEOUT这个参数,设置一个超时时间
如果B没有直接断开,那么就会给A也发送一个表示断开的数据包,请求到达A之后会发送一个答应ACK给B,A会从FIN_WAIT_2状态结束,按理来说到这里A可以断开了,但是如果这个ACK万一B收不到呢?则B会重新发一个表示断开的数据包给A,这个时候如果A已经断开,那么B就再也收不到ACK了,因而TCP协议要求A最后等待一段时间TIME_WAIT,这个时间要足够长,长到如果B没收到ACK的话,能够一直等到B重发,并A再次发送
等待时间设置为2MSL,MSL是Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃
还有一种异常的情况就是,B超过了2MSL的时间,依然没有收到它发送的FIN的ACK,按照TCP的原理,B当然还是会重发FIN,这个时候A再收到这个包之后,A就直接发送RST,B就知道A早已断开了
TCP状态机
将连接建立和断开的这个时序状态综合起来,就是著名的TCP的状态机
累计确认
在TCP里,接收端会给发送端报一个窗口的大小,叫Advertised window.
发送端需要保持的数据结构
- LastByteAcked:第一部分和第二部分的分界线
- LastByteSent:第二部分和第三部分的分界线
- LastByteAcked+AdvertiedWindow:第三部分和第四部分的分界线
接收端需要保持的数据结构:
- MaxRcvBuffer: 最大缓存的量
- LstByteRead: 已经接收了,但是还没被应用层读取的
- NextByteExpected: 第一部分和第二部分的分界线
NextByteExpected 和 LastByteRead的差其实是还没被应用层读取的部分占用掉的MaxRcvBuffer 的量,我们定义为A.
AdvertiedWindow其实就是MaxRcvBuffer减去A
也就是说:AdvertiedWindow = MaxRcvBuffer-((NextByteExpected-1)-LastByteRead).
顺序问题与丢包问题
还是刚才的图,在发送端看来,1/2/3已经发送并确认;4/5/6/7/8/9都是发送了还没确认;10/11/12是还没发出的;13/14/15是接收方没有空间,不准备发的.
在接收端看来,1/2/3/4/5是已经完成ACK,但是没有读取的;6/7是等待接收的;8/9是已经接收,但是没有ACK的.
发送端和接收端当前的状态如下:
- 1/2/3没有问题,双方达成一致
- 4/5接收方说ACK;但是发送方还没收到,有可能丢了,有可能在路上
- 6/7/8/9肯定都发了,但是8/9已经到了,但是6/7没到,出现了乱序,缓存着但是没办法ACK
假设4确认到了,不幸的是,5的ACK丢了,6/7的数据包丢了.
那么这会引起一种是超时重试,另一种是超时间隔加倍.
超时重试是指,对每一个发送了,但是没有ACK的包,都有一个定时器,超过了一定的时间就重新尝试.这个时间的评估就由自适应重传算法决定.TCP需要采样RTT的时间,然后进行加权平均,算出一个值,而且这个值是要不停变化的,因为网络状况不断的变化.除了采样RTT,还要采样RTT的波动范围,计算出一个估计的超时时间.
超时间隔加倍指,如果过一段时间,5/6/7都超时了,就会重新发送.接收方发现5原来接受过,于是丢弃5;6收到了,发送ACK,要求下一个是7,7不幸又丢失了.当7再次超时的时候,又需要重传的时候,TCP的策略是超时间隔加倍.每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为钱值的两倍.两次超时,就说明网络环境差,不宜频繁反复发送
流量控制问题
在对于包的确认中,同时会携带一个窗口的大小.
我们假设窗口始终为9.4确认来的时候会右移一个,这个时候第13个包也发送了.
这个时候,假设发送端将10//11/12/13全发送完毕,之后就停止发送
当对于包5的确认到达的时候,在客户端相当于窗口再滑动一格
如果接收方实在处理太慢,导致缓存中没有空间了,可以通过信息修改窗口的大小,甚至可以设置为0,则发送方将暂时停止发送.
我们假设一格极端的情况,接收端的应用一直不读取缓存中的数据,当数据包6确认后,窗口大小就不能再是9了,就要缩小一格变为8
这个心的窗口8通过6的确认消息到达发送端的时候,窗口并没平行右移,但LastByteAcked右移了,窗口的大小从9改成了8
如果接收端还是一直不处理数据,那么窗口越来越小, 直到0
当这个窗口通过包14的确认到达发送端的时候,发送端的窗口也调整为0,停止发送
这种通过接收方来告诉发送方调整窗口大小的行为叫流量控制
拥塞控制问题
滑动窗口(rwnd)是怕发送方把接收方缓存塞满,而拥塞窗口cwnd,是怕把网络塞满
公式LastByteSent - LastByteAcked <=min{cwnd,rwnd} ,是拥塞窗口和滑动窗口共同控制发送的速度
TCP拥塞控制就是在不堵塞不丢包的情况下,尽量发挥带宽.
通道的容量= 带宽* 往返延迟
TCP的拥塞控制主要来避免两种现象, 包丢失和超时重传.
一开始的时候TCP发送数据的时候会慢启动,cwnd设置为一个报文段,一次只能发送一个;当收到这一个确认的时候cwnd加一,于是一次能够发送两个;当这两个的确认到来的时候,每个确认cwnd加一,两个确认cwnd加二,于是一次能够发送四个;当这四个的确认到来的时候,每个确认cwnd加一,四个确认cwnd加四,于是一次能够发送八个.可以看出这是指数性的增长.
涨到超过ssthresh设置的值,即65535个字节的时候,再慢下来.
每收到一个确认后,cwnd增加1/cwnd,我们接着上面的过程来,一次发送八个,当八个确认到来的时候,每个确认增加1/8,八个确认一共cwnd增加1,于是一次能够发送九个,变成了线性增长
以上是关于TCP的主要内容,如果未能解决你的问题,请参考以下文章