TCP报文段的首部格式
· TCP首部长度为20个字节的固定首部+40个字节的可选项。
以4字节为计算单位,即首部长度可以被4整除,不够就填充。
· 具体字段含义如下:
源端口和目的端口:各占2字节。即发送方和接收方的端口。
序号:占4字节,TCP连接中传送的数据流中的每一个字节都标上一个序号,序号字段的值指的是本报文段所发送的数据的第一个字节的序号。
确认号:占4字节,是期望收到的对方的下一个报文段的数据的第一个字节号。若确认号=N,则认为序号为N-1为止的所有字节都已被正确收到。
数据偏移:就是首部长度,因为TCP首部包含了可选项,最长为40字节。它指出数据部分离起始处有多远。
保留字段:占6位,目前为0,保留为日后使用。
URG紧急:
ACK确认:
PSH推送:
RST复位:
SYN同步:
FIN终止:
窗口:
校验和:
· 紧急指针:
· 在选项里面,有一个很重要的选项——最大报文段长度(Maximum Segment Size,MSS),它用来告诉对方TCP自己的缓冲区能容纳的单个报文段的数据字段最大长度。注意和窗口区分开来,这个是单个报文数据段长度,窗口是报文段数量。
注意区分MSS和MTU:
一般来说,MSS=1460,因为以太网MTU=1500,TCP固定首部为20字节,IP首部为20字节。
规定MSS的主要目的是提高网络传输效率,MSS长度小的话网络利用率就比较低,MSS长度很长的话就要分片,增加网络开销,出错还得重传。因此,MSS的设置要尽可能大,但不要分片。
· 选项里的另一个重要选项——窗口扩大选项:占3字节,其中有一个字节表示位移值S。原来的窗口占16位,加入这个选项之后就变成16+S位:
· 还有一些其他选项;
· 最后一个填充字段:没什么意义,就是为了让整个TCP首部长度是4的整数倍。
TCP可靠传输的实现
以字节为单位的滑动窗口
· TCP的滑动窗口是以字节为单位的,虽然我们前面是以分段为单位介绍滑动窗口,但其实都是一样的。
· 发送窗口:在没有收到确认的情况下,可以连续发送的数据。
· 接收窗口:只允许接收落入窗口内的数据。
· 我们来看一个例子,假设A发送数据,B接收数据,根据B给出的窗口值,A构造自己的发送窗口长度为20字节:
从图中我们可以知道,B刚刚给A发送了一个确认,其中确认号是31,所以31之前的数据都已经确认,故A不必保留;而31也是B期望收到的下一个字节的序号,因此,根据发送窗口长度为20,A可以连续发送的字节的序号是31~50。
接下来我们可以看到,A发送了31-41号字节数据,但这11个数据还没收到确认,此时发送窗口仍不移动,剩下的42-50(P3-P2)是允许发送但还未发送,我们把这一段称为可用窗口。
视角换到B这一边,我们看到B的接收窗口长度也是20个字节,这只是为了方便演示,实际情况接收和发送窗口长度可以不一样。从图中我们发现,32和33号字节没有按序收到,因此,要回复的确认号只能是31。
接下来,我们假设刚刚B正确收到了32和33,并且给A回复了确认号为34的确认报文,此时,A的发送窗口就可以向后滑动3个序号(因为之前是31),B也同时向后滑动3个序号。然后A发送了34-41号字节,B在接收的时候37、38和40号没有按序收到,因此把其他按序收到的字节暂存在接收窗口,这三个错误的字节要求A重发,具体实现后面会讲。
接下来这种情况,A把发送窗口能发的字节都发出去了,有效窗口为0,一直没收到B的确认,此时可能是因为网络堵塞,所以A必须停止发送,从而控制流量。因此TCP是可以通过发送窗口来控制流量的。
· 窗口与缓存的关系:发送/接收窗口通常只是发送/接收缓存的一部分。
发送方应用程序把字节流写入TCP发送缓存中。
接收方应用程序从TCP接收缓存中读取字节流。
· 发送缓存与接收缓存的作用:
· 注意几点:
· 接收方发送确认:
超时重传时间的选择
· 重传机制是TCP最重要、最复杂的机制之一,因为需要跨越多个中间节点,每个节点的延时是不一样的。
· 在设置超时重传的时间时:
若太短,就会引起很多不必要的报文重传,加大网络负荷。
若太长,网络空闲时间加大,降低传输效率。
因此,TCP使用了一种自适应算法,它记录一个报文的发出和收到确认的时间,二者之差就是报文段往返时间RTT。
· 加权平均往返时间:
· 有了RTT_S后就可以计算超时重传时间RTO了:
选择确认SACK
· 如果收到的报文段无差错,只是没有按序到达,中间还缺少一些序号的数据,为了只重传缺少的数据,我们应该使用选择确认(Selective ACK)。
· 具体来说:
· 不连续的字节块的边界指的是确认号之后的字节块,如下图的第一个字节块,左边界为L1=1501,右边界为R1=3001,那么发送方收到之后,就可以知道1001-1500没有被正确接收。
TCP流量控制
利用滑动窗口实现流量控制
· 之前我们已经提过基本思想了,就是发送方的发送窗口里的可用窗口为0的话就不能再发送了。
· 一个简单的例子如下:
可以发现,接收方的接收窗口的大小是可以随时变化的。
· 可能发生死锁:在上面的例子中,我们发现,在倒数第二个报文中,B告诉A的接收窗口大小rwnd=0,那A就不会继续发送数据了,若过了一段时间后,B的缓存又有空间了,希望A继续发送,故发送最后一个报文rwnd=400,但很不幸的是这个报文丢失了,此时,A在等待B的开始信号,B在等待A的数据,双方都在等待,发生了死锁。
- 为了解决这个问题,TCP为每一个连接设有一个持续计时器(Persistence Timer)。
TCP的传输效率
· TCP有三种控制发送时机的方式:
如何控制TCP的发送时机是一个较为复杂的问题。