TCP协议实现
之前有篇文章给大家介绍了TCP报文的格式以及各字段含义,下面介绍TCO协议在连接时候如何发挥作用?
1.TCP的连接确立
(1)滑动窗口
滑动窗口以字节为单位,
假设m方收到了n方的确认报文段,其中窗口是8字节,确认号是21(表示字节序号为21之前的报文都已经收到),希望接下来接收21,于是m方根据该消息构造自己的发送窗口;
发送窗口在前面文章已经介绍过,就是窗口大小的字节数据一次性挨个连续发送,然后对方每确认收到一个字节,发送窗口前移一个字节,窗口越大,一次性发送数据越多。
与之相对的,n也有自己的接收窗口;
如图,接收窗口中允许接收,按顺序接收,每次收到一个字节就发送一次确认,进而发送方窗口向前前移一位,这样就有新数据可以发送。如果没有接受到则发送已收到的连续有序序列的最大序号加一为确认号,
如图,字节序号为22,24的没有收到,发送确认报文段的确认号为22,代表22之前的连续字节号数据全部收到 。
如果出现意外情况,即全部收到数据,但发送的确认报文段丢失或者还在路上,
在发送方中设置了超时计时器,经过一段时间后,发送方没有收到确认会自动重传。
2.缓存与窗口的关系
应用程序将发送数据写入TCP发送缓存,接收方从接收TCP缓存读取数据;
发送方和接收方都有缓存和窗口,发送缓存用来存放:
(1)发送方应用程序发给发送方TCP准备发送的数据。
(2)发送方已经发出还没有收到确认的数据。如上上张图接收窗口的绿色部分字节序号21,22,23,24。
接收缓存用来:
(1)按序到达,还没被应用程序读取的数据。
(2)没按序到达的数据。等待收到没按序到达数据后交给接收方应用。如上上张图接收方窗口的红色部分字节序号22,24。
发送窗口根据接收窗口设置,由于网络延迟,所以不会同时变化。
要点归纳:
发送缓存中已经确认的字节数,数据会被及时删除,发送缓存后沿和发送窗口后沿重合。发送应用程序必须控制写入缓存的速率,否则没有空间存放数据。
接收方的接收缓存相应的,应用程序要及时读取其中的数据,因为只有已经被读取的数据才会被删除。这样接收缓存会减小,相应的接收窗口会变大。
3.超时重传时间的计算
一个报文发出的时间以及收到确认的时间
两者之差即报文往返时间RTT;
TCP使用的加权报文往返时间RTTS,又加平滑的往返时间,每次收到一个RTT样本,就重新计算一次RTTS(S即smoothed,平滑的)
新的RTTS=(1-a)*(旧的RTTS)+a*(新的RTT样本)
a值范围:0
现在RFC6298的推荐标准是a取值0.125;
计算出来的加权平均往返时间RTTS更加平滑。
超时计时器设置的超时重传往返时间RFO(retransmission time-out)应该略大于上面的RTTS,
RFC6298推荐下列公式计算:
RFC=RTTS+4*RTTD
RTTD是RTT的偏差的加权平均值,RFC6298标准给出的RTTD计算如下:
第一次RTTD取值为测量到的RTT样本值的一半。
以后测量中使用公式为:
新的RTTD=(1-P)*(旧的RTTD)+P*|RTTS-新的RTT样本|
P是小于1的系数,推荐值是0.25;
如上图所示,假设发出的报文过了发送方计时器设置的超时重传时间还没有收到,那么就进行重传,之后收到了确认,如何知道该确认是对重传后的报文的确认还是对之前的报文的确认;
这直接影响了RTTS和RTO的计算,
根据以上题目的结论,只要报文段重传了,就不采用其往返时间样本,从而时间更加准确。
但是超传时间RTO更新就无法进行,因此我们这样处理:
报文每重传一次,就重传时间RTO扩大两倍,不再重传时候,使用上面重传时间计算公式计算即可。
选择确认SACK
如图所示。接收到的字节流不连续,缺少1001-1500,3001-3500;还有4501开始的。
每个和前后字节不连续的部分字节流使用两个指针标记边界,左指针标记左边界,右指针标记右边界,左边界指出字节块的第一个字节编号,右边界指向字节块最后一个字节编号+1;
RFC2018规定要使用SACK;Y要在TCP首部的选项字段添加“允许使用SACK”的字段,双方必须事先商定,如果允许使用SACK,那么原来首部的“确认号”字段用法不变,只是在以后的报文首部添加了“SACK”选项;
由于指明一个边界的序号要4字节(TCP首部序号字段),4个字节块有8个边界,需要使用32个字节描述,还需要两个字节,一个字节用来指明是SACK选项,另一个字节指明选项占用字节数。指明4个边界需要34(32+2=34)个字节一共,,如果是5个边界需要42个字节,超过了TCP首部选项字段的上限。