第十五章:传输控制协议(TCP)

1:基础

1:在UDP中,进程把已经定义好边界的报文发送给UDP进行发送,从进程发过来的报文称为用户数据报,并最终称为IP数据报,各个数据报之间不存在联系

2:在TCP中,进程以字节流的方式传递数据,TCP是全双工通信,每一方都有自己的发送缓存和接收缓存,这些缓存是差错控制,流量控制,拥塞控制的基础

3:进程以字节流方式将数据发送给运输层,运输层将若干个字节组成一个报文段,发送给网络层,并形成IP数据报发送出去

4:TCP通信要先建立一条虚连接,然后交换数据,最后终止连接

 

2:特点

1:字节号

1:TCP把要发送的数据按字节编号,编号不一定从0开始,而是0~2的32次方-1的一个随机数开始,由于TCP是全双工通信,两个方向的编号是独立的

2:序号

1:传输层将字节流分成报文段,每个报文段的序号就是报文段第一个字节的字节号

2:当一个报文段携带数据和控制信息(捎带)时,使用一个序号,当报文段只携带控制信息时,可能会占用一个序号,以便接收方确认,这种报文用于连接建立,连接终止,连接异常终止

3:确认号

1:报文段中的确认号是发送方希望收到的下一个分组的序号(也就是下一个数据字节号),确认号是累积的,也就是说确认号A表示A之前的数据都已经收到

4:流量控制,差错控制,拥塞控制

1:流量控制,差错控制都是面向字节的

2:发送方能发的数据量受流量控制和拥塞控制

 

3:TCP报文段格式

1:TCP首部长20~60字节,可变部分是选项和填充

2:字段包括:源端口地址,目的端口地址,32位序号,32位确认号,

首部长度:20~60字节之间

控制:,可以定义此报文段类别,如同步序号,包含确认号,紧急指针有效,终止连接等

窗口大小:16位,也就是最大65535字节,这个值被称为接收窗口,由接收方确定

检验和:同样包含IP伪首部+TCP首部+数据部分,UDP检验和是可选的,但TCP是必须计算的

紧急指针:当控制中的紧急标志置位时有效,此时报文段中含有紧急数据,此数值+报文段序号=报文段紧急数据最后一个字节的字节号

选项和填充:0~40字节,后面解释

 

4:TCP建立连接

1:三向握手

1:客户端发送SYN报文段,控制中的SYN标志置1,此报文段作用是同步序号,客户端选择了一个随机的序号,并作为此报文段序号发送给服务端,此报文段不包含确认号和窗口大小,只有当一个报文段中包含了确认号时,定义窗口大小才是有意义的,因为要发送确认号,也就是接收到数据,接收窗口会产生改变,此时告诉对方自己的接收窗口大小才有意义,此报文段还可以包含一些选项,SYN报文段是一个控制报文段,不携带数据,但它要消耗一个序号,以后发送数据时序号要加1

2:服务器发送SYN+ACK报文段,控制中的SYN和ACK标志置1,此报文段同样同步服务器序号,由于包含了确认ACK,则要告诉客户端自己的接收窗口大小,SYN+ACK报文段不携带数据,但要消耗序号

3:客户端发送ACK报文段,告诉服务端自己的接收窗口大小,此报文段一般不携带数据,所以不消耗序号,也就是使用同1所使用的一样的序号;但在有些实现中,它携带客户端的第一个数据块,这种情况下会消耗序号

2:同时打开

当两个进程都发出主动打开请求时,会有些特殊情况,后面解释

3:SYN洪泛攻击

1:原理:攻击者伪造自己的IP,不断向服务器发送SYN报文段,此时服务器不断回SYN+ACK报文段,并分配了一些资源(如创建传送控制块(TCB)表,计时器),当数量足够时,服务器没有时间处理正常的连接,这类攻击称为拒绝服务攻击

2:解决方法:

(1):限制单位时间连接次数

(2):过滤IP地址

(3):使用Cookie,做到推迟资源分配,直到服务器能够证实连接请求来自合法的IP地址,SCTP采用了这种策略

 

5:TCP数据传送

1:数据传送是双向的,一个报文段携带数据的同时也能携带确认号

2:如果一个报文段不携带数据,则它不占用序号,比如:前一个报文段发送的数据字节号为1000~2000,则下一个不携带数据的报文段序号为2000

3:发送带数据的报文段时,可将控制的PUSH标志置1,以表示推送数据

4:推送数据:

(1);发送方TCP会定义报文段的长度,并缓存应用层传输过来的字节流,当缓存达到数据报长度,才会发送一个报文(当然,这应该有其他规则,比如设置了计时器,防止一直达不到此长度就不能发送数据),接收方TCP也会将接收的数据缓存,并在接收TCP认为方便的时候才让进程拉取数据

(2):当进程间需要快速响应时,上面的策略就显得不合理,推送数据置1表示发送方应当尽快发送数据不要等待更多的数据到达,接收方应该尽快准备好数据当应用层拉取数据而不是等待更多的数据到达

(3):TCP实现大多忽略了推送数据此功能

5:紧急数据

(1):如果一个数据段控制的紧急指针位置1,表示此数据段携带有紧急数据,紧急数据从此数据段第一个字节开始,首部中的紧急指针字段定义了紧急数据最后一个字节的字节号

(2):携带有紧急数据的数据段和普通数据段对于应用层以下的各层没有任何的不同,它只起一个标志作用,真正区别它的是应用层

 

6:TCP连接关闭

1:三向握手(全关闭)

1:A向B发送报文段请求关闭连接,此报文段控制的FIN位置1,此报文段可以携带数据,如果不携带数据,则占用一个序号

2:B向A发送ACK+FIN报文段,同样可携带数据,如果不携带数据,则占用一个序号

3:A向B发送ACK报文段,不消耗序号,也不能携带数据

2:四向握手(半关闭)

A请求关闭,但B推迟发送ACK+FIN报文段,而是继续发送数据报文段,A可以接收数据和发送ACK报文段,但不能发送数据报文段

1:同三向握手1

2:B发送ACK报文段,表示同意关闭连接,然后B继续发送数据报文段,A回复ACK报文段

3:同三向握手2

4:同三向握手3

 

7:连接复位

连接复位采用控制的RST(复位)标志完成,它有以下功能

1:拒绝连接请求

A的TCP向一个不存在的端口发送连接请求,另一端TCP就可能发送RST==1的报文段拒绝这个请求

2:异常终止连接

A出现异常,并希望放弃一条在用的连接,则发送RST==1报文段终止这条连接

3:终止空闲连接

A发现一条连接长时间空闲,则发送RST==1报文段终止这条连接,该过程同异常终止连接是一样的

 

8:状态转换图

接下来从整体的角度说明连接的生命过程和特殊情况

1:连接建立(一般正常情况下)

A发送SYN,B回复SYN+ACK,A回复ACK

2:连接建立(双方同时连接)

双方同时发送SYN,双方都受到SYN后,又都同时回复SYN+ACK,此时连接建立

3:拒绝连接

A发送SYN,B回复RST+ACK

4:连接关闭(四向握手半关闭)

1:A发送FIN,B回复ACK,B继续发送数据,A可以回复ACK但不能发送数据,B发送FIN+ACK,A回复ACK并启动计时器,计时器超时就关闭连接,释放资源

2;之所以要设定计时器,是因为B发送了FIN+ACK会启动一个计时器并等待A的ACK(对于所有的报文段,都会启动计时器,参考选择重发协议),如果A一收到FIN+ACK发送ACK后就关闭自己,如果此ACK丢失,B的计时器就会不停的超时重发FIN+ACK但又永远也等不到ACK,所以这里A设定一个计时器,如果在计时器内没有再收到FIN+ACK,则认为对方已经收到ACK了

3:计时器超时时间是最大报文段寿命(MSL)的两倍,MSL是一个报文段被丢弃之前在因特网中能够生存的最大时间,MSL常用数值为30~60秒

5:连接关闭(三向握手全关闭)

A发送FIN,B回复FIN+ACK,A回复ACK并设置MSL

6:连接关闭(同时关闭)

双方都发送FIN,然后双方都回复ACK并设置MSL,超时后都关闭

7:化身

AB通信,然后关闭连接,马上又开始新连接,使用同样的套接字(源IP,目的IP,源端口,目的端口),这样的新连接称为旧连接的化身,则新的连接可能会收到上一次连接的数据,为了避免这个问题,TCP协议规定化身必须在2MSL之后出现,但在一些实现中,如果化身使用的初始序号大于之前连接最后的序号,也可以不需要此限制

8:异常终止连接

A发送RST+ACK报文段,立即进入终止状态,丢弃缓存中所有数据,B收到此报文段,同样进入终止状态,并丢弃缓存所有数据

 

9:TCP中的窗口

1:发送窗口

1:发送窗口会收缩,大小由接收方和拥塞控制决定的,TCP滑动窗口采用字节编号

2:在Windows中,参考我的其他文章,在发送窗口和进程缓冲区之间,还有一个缓存

3:TCP协议只使用一个计时器

2:接收窗口

1:接收窗口不会收缩,接收窗口能从发送方获得的数据大小称为rwnd

2:rwnd=缓存大小-缓存中等待进程拉取的数据字节数,缓存大小一般实现为几千字节

3:TCP确认号机制是累积确认,在新版TCP中同时使用累积确认和选择确认

4:补充说明一点:以Windows为例,接收窗口的缓存大小就是起操作系统核心TCP层实现的缓存大小,看一些资料应该是17KB,接收窗口可能不需要应用层设置,它直接设置为操作系统TCP层缓存最大值,这样效率是最高的

 

10:流量控制

1:数据整个流动情况是:发送进程推送数据到运输层,运输层推送数据到接收方运输层,接收方拉取运输层数据到应用层,所以流量控制在两方面:

(1):发送方运输层反向控制发送方进程,通过对Send()函数返回失败来控制

(2):接收方运输层反向控制发送方运输层

2:接收方指定rwnd值可能会导致发送窗口的缩小,此时发送窗口右壁向左收缩,有些实现强制发送窗口不能收缩(只是右壁不能向左移动),如果没有此限定,要注意一个问题:

发送窗口右壁的收缩可能会导致已经发送出去的数据落在了发送窗口外面,要避免这种情况的一种方法是:接收方延迟提交接收窗口大小,直到窗口比较大为止

3:窗口关闭:接收方可以发送接收窗口大小为0的报文段用来暂时关闭窗口,窗口关闭一般用于接收方暂时不希望接收数据的情况,收到此报文段后,发送方不会真正将自己的窗口大小设置为0,但会暂停发送数据,直到收到一个新的报文段为止,这期间,其实发送方仍然能够发送具有一个字节的报文段,称为探测,用于防止死锁

 

11:糊涂窗口综合症

1:名词解释

当发送方数据以很小的方式产生或者接收方数据以很小的方式消耗,对于每一个小量数据都会形成一个报文段,其中首部远大于数据部分,这样带宽的利用率是非常低的,这就是糊涂窗口综合症

2:发送方解决方案:Nagle

采用Nagle,在Windows平台下的实现是:如果发送缓冲暂存的数据超过了最大报文段则立即发送,不然等待上一个ACK到达再发送下一个报文段

3:接收方产生原因

当接收缓存满时,接收方进程Recv()一块很小的数据,导致发送方同样发送一块很小的数据

4:接收方解决方案

(1):Clark:接收方只要数据到达就返回ACK,但设定一个阙值,低于此值返回的ACK窗口大小设置为0

(2):推迟确认:接收方数据到达不立即发送ACK并设置一个计时器(500ms),让接收缓存有机会释放空间,当超时或者空间大于最大报文段时发送ACK,此方法如果和网络状况吻合能节约带宽,如果不吻合有可能造成数据重发

5:Windows平台下可能产生的例子

当设置发送缓冲为0,启用Nagle算法,应用层投递20000个小包,则每一个小包都会对应一个报文段,并且要等待ACK才能发送下一个报文段,显然这样的效率是非常低下的

 

12:差错控制

1:确认

1:控制报文段不携带数据,但要消耗一个序号,它也需要被确认

2:确认ACK不需要被确认,也不消耗序号

3:确认类型:

(1):累积确认ACK

(2):选择确认SACK:SACK要报告失序的数据块和重复的报文段块,由于TCP首部没有空闲放这些东西,所以SACK通过TCP首部末尾的选项来实现

2:产生确认的规则

不同的实现有不同的规则,以下这些是最常用的,顺序不代表其重要性

1:A向B发送的数据报报文段,必须捎带一个确认

2:A收到B的正确报文段,但A没有想发给B的数据,同时前一个报文段也已经确认过了,那么以下两种情况会立即发送确认:

(1):又收到一个B的报文段

(2):超时(一般是500ms)

3:A收到一个正确的报文段,但前一个报文段还没确认,立即发送ACK,也就是不能有两个以上按序到达的报文段未被确认,这和2(1)实现方式不同,2(1)是只要收到一个报文段,不管是不是按序到达的,都会发送确认

4:A收到比自己期望收到的序号S大的报文段,立即发送ACK(S),这会导致对丢失报文段的快重传

5:当一个丢失的报文段到达,马上发送ACK+自己期望的下一个序号

6:当一个重复的报文段到达,丢弃报文段,马上发送ACK+自己期望的下一个序号

3:重传

1:发送方为每条连接设置一个重传超时(RTO),当RTO超时,则发送序号最小的已发报文段并重启计时器,RTO值是动态的,它根据报文段往返时间(RTT)更新

2:如果发送方收到三个相同的ACK,则立即发送报文段而不必等待RTO超时,这成为快重传

4:失序

TCP不会丢弃失序到达的报文段,也不会提交失序的报文段给进程

 

13:TCP数据传送流程图

1:正常运行

接收方收到一个包,如果没有数据发送给发送方,则启动ACK延迟计时器(Windows下200ms),在超时之前如果又收到报文段,则立即发送ACK;如果超时,同样发送ACK

2:报文段丢失

发送方发送第一个报文段时,启动RTO计时器(重传计时器),如果RTO超时,则重传序号最小的未被确认的报文段,RTO具体参考后面

3:快重传

当RTO计时器超时之前,发送方发送了多个报文段,假如第一个报文段丢失,接收方接收后面几个,每次都会收到报文段立即发送ACK,当发送方收到三个ACK并且具有相同的确认值,则立即重传报文段并重置RTO

4:延迟的报文段

发送方发送报文段A,超时后重发A*,A*可能比A更先到达接收方,接收方接收了A*后,如果A到达,会被抛弃

5:重复的报文段

同4基本一样,丢弃后到的报文段

6:自动纠正丢失的ACK

由于TCP的ACK是累积确认,当前一个ACK丢失,后一个ACK可能会确认更大的数值,这样的效果是前一个ACK的丢失是完全没影响

7:因确认丢失而产生的死锁

接收方发送ACK并设置rwnd=0,请求发送方暂时关闭窗口停止发送,当接收方希望恢复发送时,会发送一个rwnd!=0的ACK,但此ACK可能会丢失,导致发送方在等待此ACK,接收方在等待数据的死锁情况

死锁:双方都在等待对方的响应

解决方法:设置持续计时器,后面讨论

8:备注

如上讨论,发送方有RTO计时器,接收方有ACK延迟计时器,当通信是双向时,一方根据实际情况可能会有相应计时器产生

 

14:拥塞控制

 拥塞控制分为开环拥塞控制和闭环拥塞控制

1:拥塞窗口

1:除接收方能控制发送方发送窗口外,网络是控制发送方窗口大小的第二个实体

2:真正发送窗口大小=min(rwnd,cwnd);rwnd:接收窗口;cwnd:拥塞窗口

2:拥塞策略

1:拥塞策略处于三个阶段:慢开始,拥塞避免,拥塞检测

2:综述:慢开始阶段,发送方以非常小的速度开始发送数据,但很快把速度增加到一个门限值,然后进入拥塞避免阶段,数据率增长开始放慢,最后,只要一检测到拥塞,发送方又回到慢开始或者拥塞避免阶段

3:慢开始:拥塞窗口大小从1个最大报文段的长度(MSS)开始,MSS的数值是在建立连接时同名选项确认的,每当到达一个ACK,cwnd就加1,发送窗口就会扩大,由此可知,cwnd会以指数增长,当到达ssthresh(慢开始门限)时,此阶段结束

4:拥塞避免(加法增加):拥塞窗口大小按照加法规律增长,直到检测到拥塞为止;每当一个窗口的数据被确认后,拥塞窗口才+1;和慢开始不同的时,此算法针对窗口,而慢开始针对单独数据报

5:拥塞检测(乘法减少):发送方能够检测到拥塞已经发生的唯一现象就是需要重传一个报文段,可能是RTO超时或者收到连续三个相同ACK

当一个报文段被路由器丢弃后,会发送ICMP差错报文,但这里不通过ICMP检测报文的丢失,而是通过RTO超时

此时TCP实现一般会有以下两种反应:

(1):如果是RTO超时,拥塞可能性很大,TCP有以下强烈反应

A:门限值设定为当前窗口大小的一半

B:把cwnd重新设置为一个报文段

C:状态转移到慢开始

(2):如果是收到3个ACK,拥塞可能性较小,TCP有以下较弱反应

A:门限值设定为当前窗口大小的一半

B:把cwnd设为门限值(也就是当前窗口大小的一般)

C:启动拥塞避免阶段

 

15:TCP的计时器

1:RTO重传计时器

按照书上的理解是:当发送发送窗口中第一个报文段时,启动此计时器,当这个计时器超时后,重发发送窗口第一个未被确认的报文段,当超时时发送窗口为空,则停止此计时器

但是我想不通的是:如果发送一直在进行,随着窗口滑动,发送窗口一直有数据,那将肯定会超时,这时是如何处理的,留着这个疑问在这里了

1:RTT,报文段往返时间即RTT,在TCP中,任何时刻都只能有一个正在进行的RTT测量,测量RTT需要用到选项的时间戳

2:RTT采用加权平均进行计算,RTO=RTT加权平均+RTT偏差平均

3:Karn算法:RTT由于要采集报文段往返时间,如果一个报文段被超时重传,当收到ACK时不知道是旧报文段的ACK还是重传报文段的ACK,Karn采用一种简单的方法:在计算RTO时不采用重传的RTT

4:指数退避:如果发生重传,RTO的数值就加倍

2:持续计时器

1:接收方发送rwnd=0的ACK给发送方,通知发送方暂时关闭窗口,停止发送报文段,当接收方想继续接收取报文段时,发送rwnd!=0的ACK,由于确认报文段不能被再次确认,并且不会设置RTO重传计时器,所以当此ACK丢失后,会造成死锁

2:为了解决死锁问题,当发送方收到rwnd=0的报文段时,启动持续计时器,超时默认值为RTO大小,当超时时,发送1字节数据的探测报文段,此报文段有序号,但此序号不需要被确认,并且以后发送数据也可以重用此序号,如果接收方已经发送了rwnd!=0的ACK,当接收方收到探测报文段时,会重发此ACK,如果接收方想维持rwnd=0,则不响应,此时,发送方会将持续计时器的值加倍并重启计时器,如果往返,当持续计时器值达到一个门限则不再增加(通常60s),在这以后,发送方每隔60秒发送一次探测报文段,直到窗口被打开

3:保活计时器

1:保活计时器用于在连接长时间静默,确认连接是否有效

2:服务器设置保活计时器,当收到客户端一块数据时重置此计时器,一般为2个小时

3:超时后,服务器发送探测报文段,若连续发送10个探测报文段没收到响应,则终止这个连接

4:TIME-WAIT计时器

连接终止时使用,防止最后一个ACK丢失,当发送ACK出去后,还有FIN到达,则再次发送ACK

 

16:选项

1:最大报文段长度MSS

最大报文段长度选项定义了能够被接受的最大TCP报文段数据部分长度,该值16位,MSS在连接建立阶段确认,如果一方没有定义,则使用默认值536字节,连接期间不能改变该值

2:窗口扩大因子

首部窗口大小字段定义了滑动窗口大小,长16字节,但可能不够用,所以窗口扩大因子作用为:实际窗口值=首部窗口值*2的窗口扩大因子次方,窗口扩大因子最大255,但在TCP/IP中最大值为14,也就是2的30次方,因为窗口大小不能超过序号最大值

在数据传输过程中,窗口大小可以改变,但窗口扩大因子不能改变

3:时间戳

请求连接的一方SYN中宣布一个时间戳,SYN+ACK如果也能收到时间戳,则表示允许使用时间戳,否则不再使用

时间戳选项有两个应用:测量往返时间防止序号绕回

1:测量往返时间RTT:

 发送方准备好一个报文段,并读取系统时间,将此时间插入到时间戳字段中,接收方收到这个字段后,复制出来,当累积确认包含了对此报文段的确认时,就将此时间戳复制到选项的时间戳回送回答中,发送方收到报文段后获得当前系统时间,并减去时间戳回送回答的时间,这就是RTT

2:防止序号绕回PAWS:

序号为32位,当数据量很大时,可能发生绕回,使用序号+时间戳可以唯一的标识一个报文段

3:允许SACK和SACK选项:

TCP采用累积确认方式,但当报文段丢失,失序到达,重复到达时,累积确认不能报告这些情况,于是提出选择确认(SACK),它通过选项来报告这些错误,以使发送方能更精确的应对

此方法详情见书上

 

17:TCP软件包

对于每条连接,都会存储在一张表中,并新建一行,这个表称为传输控制块TCB

TCB包含的字段包括:

状态:连接此时的状态

进程:使用该连接的进程

本地IP,本地端口,远程IP,远程端口

本地窗口,远程窗口

发送序号,接收序号

已发送ACK号

往返时间:保存多个RTT

超时值:多个计时器的超时值

缓存大小

缓存指针


版权声明:本文为mxzy55560593原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。