1.TCP协议简介
TCP协议是TCP/IP体系中一个非常复杂的协议,在4/5/7层网络模型中属于传输层的协议,主要有以下几个特点:
1.面向连接:通信双方在传输数据之前必须通过三次握手建立连接,数据传输结束需要经过四次挥手来关闭连接。
2.提供可靠的交付:满足四个要求。无差错、无丢失、无重复、无失序
3.基于字节流:这一点挺重要,与UDP的面向报文不同,TCP的传输是基于字节流 的。
在4/5层网络模型中传输层的上层是应用层,应用层的传递是报文,所以TCP这一特性将会导致,
无法保留应用层的报文段原有边界。不管应用层传递的报文长度有多长,TCP都将会以字节为单位按序存储在TCP缓存中,
等待合适的机会将其发送出去。每次以多长的单位来发送数据则由发送窗口值以及拥塞窗口值来决定。
UDP则不同,UDP发送的报文长度完全是由应用层进程决定的。
下面插入一张TCP "基于字节流"的概念图(图1.1)来帮助大家理解。
4.只支持一对一通信,不支持多播(Multicast)和广播(Broadcast)。TCP的每一条连接有且只有一条端点,是端到端的通信,因此TCP只支持一对一的通信,无法像UDP一样支持多播和广播的交互通信。介绍一个小知识点:端点指的是套接字,用于标识网络中的唯一一个进程,因此由ip地址跟端口号组成。
5.全双工通信:一个TCP连接的两端的进程都允许发送数据和接收数据。各自都拥有发送缓存和接收缓存,发送缓存用于临时存放从应用层交下来的数据,接收缓存用于临时存放网络层交付上来的数据。
TCP的应用挺广泛,应用层的HTTP、Talent、FTP、SMTP均是采用了TCP的连接。
图1.1 TCP "基于字节流"的概念图
2. TCP报文段的首部格式
TCP报文段的首部包含固定部分和可选部分,固定部分长度是20字节,可选部分长度最多可达40字节。首部格式图如下:
首部固定部分如下(共占20字节):
(1).源端口和目的端口各占16位,用于指定双方的端口号。
(2).序号占32位,取值范围0-2的32次方 -1,用于标记发送方发送的每一个字节,序号可以循环使用。
(3).确认号占32位,取值范围0-2的32次方 -1,用于标记期望收到的下一个字节的编号。
(4).数据偏移占4位。该字段表明了报文段中数据部分距离报文开始位置有多长,也就是指明了报文中首部的长度,该字段中每一个"1"值代表4字节,
而报文段最小头部长度为20字节,所以数据偏移的取值最小为5,TCP报文的头部最大取值为60字节,因此数据偏移最大的取值为15。在报文段头部中固定部分占20字节,所以可选部分(选项和填充部分)最大长度为40字节。
(5).保留占6位,作为保留目前未使用,置为0.
(6).控制位占6位,分6种类型。
*URG(URGent,紧急):当URG=1时,表示报文中有紧急数据,紧急数据的大小由后面的紧急指针字段指定,因此这两个字段需要配合使用。TCP会将紧急数据插入到发送缓存即将要发送的数据之前,无需排队。
*ACK(ACKnowledgment,确认):当ACK=1时,确认字段有效,TCP规定建立连接之后,所有的报文段中ACK置为1。
*PSH(PuSH,推送):当PSH=1时,发送方的TCP会立即创建一个报文段发送出去,同时接收方收到PSH=1的报文段时会立即将其交付到上层的应用层,无需等待合适的时机。
*RST(ReSet,复位):有两种情况RST会置为1:主机崩溃等原因造成TCP连接出错,用RST释放原先连接并建立一个新的连接;拒绝一个非法的TCP报文段或拒绝打开或释放一个连接。除此之外RST都置为0。
*SYN(SYNchronization,同步):同步位SYN用于建立连接的过程,无论是连接建立的请求或是接收报文段都需要将SYN置为1。
*FIN(FINis,终止):终止位FIN用于TCP释放连接的过程。由于连接建立之后,无论哪一方都可以发送请求,所以释放连接也需要两个方向。不管哪一个方向释放连接,FIN都置为1,只要FIN=1,将消耗一个序号。
(7).窗口占16位,取值范围0-2的16次方 -1, 单位是字节。该字段用于告诉发送方自己的接收窗口值的大小,该字段是随着缓存窗口的大小而动态变化的,下面会有详细的讲解,暂时大体介绍一下。
(8).检验和占16位, TCP首部校验和计算三部分:TCP首部+TCP数据+TCP伪首部,用于TCP差错检测,伪首部共有12字节,包含IP首部的一些字段,有如下信息:32位源IP地址、32位目的IP地址、8位保留字节(置0)、
8位传输层协议号(TCP是6,UDP是17)、16位TCP报文长度(TCP首部+数据)。 伪首部是为了增加TCP校验和的检错能力:通过伪首部的目的IP地址来检查TCP报文是否收错了、通过伪首部的传输层协议号来检查传输层协议是否选对了。
(9).紧急指针占16位,需要配合URG紧急位,在URG=1时有效,支出紧急数据的字节数。
3. TCP可靠传输的实现
1.滑动窗口机制介绍:
a.TCP是面向字节流的,因此TCP双方各自维护一个以字节为单位的滑动窗口,用来保证双方的收发一致性。假设滑动窗口的大小是20字节,则表示一次只允许发送或接受20字节。
b.如果不存在滑动窗口机制,发送方就不知道接收方的容纳能力,如果接收方的接收缓存已经满了,存储了部分字节数据未及时向应用层发送,而此时发送方并不清楚,还会一直发送数据给接收方,接收方此时无法接收数据,数据包将会丢失,造成网络资源的浪费。
第二点,网络中存在很多主机,在同一时刻存在很多TCP连接,如果所有的发送方都可以无限制的发送数据,会导致同一时刻有大量的报文段涌入网络中,从而形成网络的堵塞,所以窗口机制非常有必要,如图2.2所示是窗口值的交互。
c.图2.2可知发送方发送完报文段之后需要等待接收方的确认,如果因为网络原因发送方没有收到接收方的确认会一直等待,不进行下一次数据的发送,这样就会形成死锁,解决办法是重传计时器机制,TCP的发送方会设置一个定时器,超过限定的最大时延会重传报文,
重传超时时间(RTO),RTO一般设定为比一次交互往返时间(RTT)多一些。
d.如果接收方的窗口值很大,但是发送方发送时机不合适,导致每次只有少量数据需要发送,这会倒是传输效率极低,解决办法是Nagle算法,发送方接收到应用层的数据之后放入缓存,先发送一个一字节的报文试探网络拥堵,在收到一字节报文确认之后再将缓存中数据组装成一个TCP报文(MSS长度)发送出去,
-利用了一个往返交互来让发送方积累足够的字节数据。
e.如果接收方的窗口值rwnd很小,则发送方每次发送很小的数据,这一现象成为”糊涂窗口综合征",解决办法:禁止发送方发送只有一字节的窗口通知,让接收方等待一会儿,知道接收缓存的空闲空间足够接收一个最大长度的报文段或达到缓存空间的一半,再通知发送方发送窗口报文。保证传输效率。
图3.1.1窗口控制示例
2.序号和确认号机制
a.TCP是面向字节流传输而非面向报文,因此对于应用层交下来的数据,TCP会对其进行拆分或者合并。TCP的序号和确认号机制是实现可靠传输服务的保障。
b.TCP一次发送多少数据是由很多因素决定的,比如发送缓存已占用空间、最大报文段长度MSS,发送窗口接收窗口的大小等。
c.发送方进行数据发送时会消耗一个序号,也就是该批次数据中字节序号最小的,接收方收到数据之后,确认位ACK置为1,回发一个确认字号ack,
用于标识接下来需要接收的开始序号,如图3.2.2所示。
图3.2.2序号和确认号机制
3.重传计时器机制
a.TCP对发送的每一个报文都设定一个计时器来限定报文发送的最大时延(RTO),超过RTO没有接收到确认就会重发该报文,当然重发的报文 如果是接收方已经收到了,就会丢弃,不会导致接收方报文重复的问题。
b.超时重传时间RTO的确定,由往返时间(RTT)决定,通常RTO比RTT多一些。
RTO的计算步骤:
4.流量控制
接收流量控制:
a.接收流量控制的目的是为了控制发送方的发送速率,是的接收方来得及接收。
b.发送缓存用于接收应用层的数据,进行分组合并等转为字节流存入发送缓存中,但并不是发送缓存中的数据都会被发送,
最终决定发送什么数据是由发送窗口决定的,发送窗口中存在两部分数据,一部分是已发送等待确认的,一部分是未发送的。
我们只需要控制发送窗口的大小就能控制流量,而控制发送窗口的是由接收方来决定的。
c.接收缓存中存储了已经接收的数据,由于应用层的处理能力的限制,接收缓存中的数据可能存在很多滞留,导致接收窗口的空间被压缩,
如果此时继续接收大量数据会造成丢失。因此接收方需要与发送方进行沟通,将接收方的窗口大小告知发送方,随时调整发送的数据量,
换句话说,只要保证接收方的接收窗口不小于发送方的发送窗口就实现了流量的控制。
网络流量控制(即拥塞控制):
a.前面介绍的接收流量控制属于一对TCP连接的端到端的问题,属于局部。对于整个网络而言存在大量的TCP连接,因此也存在拥塞控制,目的是防止过多的数据涌入网络造成网络拥塞,这是全局的问题。
b.RFC2581对TCP的拥塞控制提出了四种算法:慢开始(Slow-Start)、拥塞避免(Congestion Avoidance)、快重传(Fast Retransmit)、快恢复(Fast Recovery)
c.拥塞窗口(cwnd)是发送端根据网络拥塞决定的窗口值。接收窗口(rwnd)是接收方的接收能力。
发送窗口的上限=min{rwnd,cwnd}
d.传输轮次:假设传输轮次n的cwnd=m,则传输轮次所经历的时间是将m个报文段全部发出去的时间,所有报文段发送确认总共花费时间。
e.慢开始:慢开始算法是在第一轮次发送数据时,将cwnd置为1,发送一个报文段,每收到一个报文段将cwnd加1,一个轮次会发送多个报文段,每个报文段都会加1,因此为指数增长方式,只是开始慢,很快就会达到一个较高的值。因此引入了另外一种算法拥塞避免算法。
f.拥塞避免算法:用于制止cwnd的指数增长方式,加入一个条件慢开始门限ssthresh,当cwnd<ssthresh时,采用慢开始算法,当cwnd>=ssthresh时,采用新的算法拥塞避免算法,该算法是将每个轮次的cwnd加1,不管改轮次多少报文段都只加1,是线性增长。
新的cwnd的取值,本次cwnd<ssthresh慢开始算法,如果2*cwnd小于ssthresh则新cwnd取值2*cwnd,否则取值ssthresh;本次cwnd>=ssthresh拥塞避免算法,则新cwnd取值cwnd+1.
仍然可能出现拥塞,如果出现拥塞将采用新的慢开始算法,此时ssthresh取值拥塞是cwnd的1/2。其他的照旧。
g.快重传算法:前面两种算法可以有效的解决网络拥塞,但是存在一个问题。如何判断网络拥塞?发送端发送报文如果确认报文迟迟未收到,即触发了超时,发送端超时计时器实现。
举例:发送方发送了按序m1,m2,m3,m4,接收方没收到m3,收到了m1,m2,m4。m1,m2发送了确认报文,但是m4是失序的所以不确认依然发送m2确认报文,
此过程中相继收到了m5,m6,所以网络没有堵塞只是m3缺失,如果此时m3依然在发送缓存中排队,时间一长会触发超时机制,超时机制触发网络拥塞,导致慢开始算法的启动,很糟糕。。
快重传算法就是解决这个问题避免触发超时,只要发送方收到对同一报文的三个确认将会立即重发该报文,不必等待。如图4.1快重传示意图
h.快恢复算法:如g所示,在传输的过程中出现了m3的丢失,虽然不是网络拥塞但是这个问题也不能小觑,一定是存在网络问题才导致的报文段丢失,所以只要出发快重传算法就会出发快恢复算法,
此时快恢复算法是为了稳妥而产生的,将慢开始门槛ssthresh调整为上面第三次报文确认时cwnd的1/2,将下一轮的cwnd调整为新的ssthresh,之后开始进入拥塞避免算法阶段。如图4.2快恢复示意图。
图4.1快重传示意图
图4.2快恢复示意图