TCP虽然是面向字节流的,但是TCP传输的单元确实报文段。一个TCP报文段分为首部和数据部分。TCP首部前20个字节是固定的,后面有4N个字节是可选的。因此,TCP首部最小字节数是20个字节。
下面我们看下一TCP首部中几个重要的字段:
最大报文长度MSS(Maxium Segment Size),是每一个TCP报文段中数据字段的最大长度。而一个完整的 TCP报文段 = 数据字段 + 首部长度。
为什么要有MSS?因为TCP数据至少要添加20个字节的TCP首部和20个字节的IP首部,才能发送。所以,当发送的数据量少,如一个字节。那么网络的利用率就极低。如果数据太大,会在IP层进行分片。到接收端后还要重组。如果不幸,有数据丢失或者出错,还需要重新发送,导致开销增大。
窗口扩大选项:互联网早期,窗口大小两个字节,最大64K够用。为了满足发展需要,新增窗口扩大选项,占3个字节。其中有一个字节表示表示移位值S。新的窗口大小位数从16位增加到(16+S),S最大14,故窗口最大值增加到[0,2^30-1].
时间戳选项:占10个字节。其中主要时间戳字段和时间戳回送回答字段各占4字节。作用:(1)计算RTT;(2)用于处理序号超过2^32的情况。
选择确认选项:适用于,收到的报文段无差错,只是未按序号,中间还缺少某些序号的数据。选择确认就可以只让发送方发送这些缺少的数据,后面还会详细介绍。
Tcp的滑动窗口是以字节为单位的。
现在假设发送方A收到了接收方B的确认报文,其中确认字段是31(表示31之前的数据都已收到,需要A从31开始发数据),窗口大小是20。A会根据这两个数据构造出自己的发送窗口,如下:
发送窗口:在没有收到B的确认情况下,A可以连续把窗口内的数据都发出去。发送窗口内的序号都是允许发送的序号。
现假设A又发送了31~41的数据,如下图:
B此时的接受窗口如下:
此时,B虽然收到了32、33的分组,但是没有按序接收,所以B给出的确认只能还是31。
再假设,B按序接收到了31、32、33。B把接收窗口往后往前移动3个序号,同时给A发出确认报文,窗口值仍为20,但是确认号为34。A在收到确认报文后,将自己的发送窗口也往前移动3个序号,注意,B还收到了37 38 40三个分组的数据,但是没有按照顺序,所以先暂存在自己的接收窗口。 如下:
假设接收方收到若干不连续的报文段。序号1~ 1000收到了,但1000 ~ 1500没收到。序号1501~ 3000收到了,但30001~ 3500没收到。序号3501~4500又收到了。如果这些序号都在接受窗口内,接受方先收下这些数据,并把这些消息告诉发送方,让发送方不在发送重复数据。
我们可以看到,每个字节块都有左右两个边界,我们只需要告诉发送方这些边界信息,让其从指定序号开始传就达到我们的预期了。
但是,TCP首部没有哪个字段是来描述这些信息的。选择确认选项就派上用场了。如果要使用选择确认,就需要在TCP首部加上允许SACK选项。由于首部选项的长度只有40个字节,每个边界需要4个字节,所以,最多只能传递8个边界。为什么不是5个字节块(10个边界信息)呢?因为这40个字节,还要分出来两个字节,一个用来开启SACK;一个字节用来指出SACK选项占几个字节。
所谓流量控制就是控制发送方的发送速率,不要太快,让接收方来得及接收处理。利用滑动窗口就可以很方便的在TCP连接上实现对发送方的流量控制。
拥塞控制就是防止过多的数据注入网络,导致网络过载。注意与流量控制的区别,流量控制一般是点对点的控制。而拥塞控制是一个全局性的过程,涉及所有的主机和路由器等待。
拥塞控制方法:
发送方维持一个拥塞窗口cwnd(congestion window)的状态变量。取决于网络拥塞程度,动态变化。发送发控制cwnd的原则:只要网络没出现拥塞就增大cwnd,出现了就减小。
当网络出现拥塞时,路由器就要丢弃分组。因此,如果发送方没有按时收到应该到达的确认报文,就认为网络出现拥塞。
为方便描述,我们用报文个数作为窗口大小的单位,实际上单位是字节。慢开始的思路就是,刚开始将cwnd设置为一个MSS的数值,每经过一个传输轮次cwnd就加倍。
传输轮次:拥塞窗口所允许的发送的报文都连续发出去,且收到了对已发送的最后一个字节的确认。
因为是加倍(指数)增长,为了防止增长过度引起网络阻塞,还设置一个慢开始门限ssthresh。当cwnd超过ssthresh时,就改为拥塞避免算法,也就是线性缓慢增长。
无论是在慢开始还是拥塞避免阶段,只要发送方判断网络出现拥塞,就会把ssthresh设置为拥塞时发送窗口的一半,cwnd设置为1,重新执行慢开始算法。
快重传算法要求接收方每收到一个失序的报文段,就立即发出重复确认,目的是使发送方及早知道有报文段丢失。如果发送方连续手动三次重复的确认就立即重传对方未收到的报文。
因为可以收到连续三个重复报文的确认,发送方不认为是网络出现了拥塞。使用快恢复算法。快恢复思路是:把cwnd设置为ssthresh的一半,然后开始拥塞避免算法。
在采用快重传算法时,慢开始算法只在TCP建立连接和网络出现超时的时候使用。
上面的描述都是假定接收方总是有足够的空间接受数据。实际上接收方的空间也是有限的,接收方根据自己的接受能力设置接受窗口rwnd。因此:
发送方窗口 = MIN[rwnd, cwnd]
文章参考于<零声教育>的C/C++linux服务期高级架构