TCP/IP协议族【第15章 传输控制协议(TCP)】

来源:互联网 发布:天音网络发展有限公司 编辑:程序博客网 时间:2024/05/20 03:46

1 TCP服务


tcp位于应用层和网络层之间。

1.1 进程到进程的通信

   与UDP一样,TCP也是用端口号提供进程到进程的通信。

1.2 流交付服务

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

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

TCP通信要先建立一条虚连接(虚拟管道),然后交换数据,最后终止连接。

发送缓存、接受缓存:用来存储数据,还可以用来进行流量控制,差错控制。

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

1.3  全双工通信

1.4 复用和分用

与UDP一样,TCP在发送端执行复用,接收端执行分用。

1.5 面向连接的服务

TCP是面向连接的协议,需要经历3个阶段
1.这两个TCP在它们之间建立一条虚链接。
2.数据在两个方向交换。
3.连接被终止。

1.6 可靠的服务

后面有差错控制介绍。

2 TCP的特点

2.1 编号系统

字节号TCP把要发送的数据按字节编号,编号不一定从0开始,而是0~2的32次方-1的一个随机数开始,由于TCP是全双工通信,两个方向的编号是独立的。
序号:传输层将字节流分成报文段,每个报文段的序号就是报文段第一个字节的所分配的字节号。

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

确认号报文段中的确认号是发送方希望收到的下一个分组的序号(也就是下一个数据字节号),确认号是累积的。

2.2 流量控制:

面向字节

2.3 差错控制:

面向字节
2.4 拥塞控制:

发送方能发的数据量受(接收方的)流量控制和拥塞控制

3 报文段

3.1 格式



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

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

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

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

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

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


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

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

3.2 封装

TCP报文段封装了从应用层接受到数据,它本身也封装在IP数据报中,然后在封装成数据链路层的帧。

4 TCP链接

4.1 链接建立

三向握手

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

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

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

同时打开
当两个进程都发出主动打开请求时,会有些特殊情况。
SYN洪泛攻击

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

2:解决方法:

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

(2):过滤IP地址

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

4.2 数据传输


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

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

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

推送数据:

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

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

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

紧急数据

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

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

4.3 链接终止

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


4.4 连接复位

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

1:拒绝连接请求

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

2:异常终止连接

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

3:终止空闲连接

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


5 状态转换图

一般情况
A发送SYN,B回复SYN+ACK,A回复ACK

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

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  max segment lifetime)的两倍,MSL是一个报文段被丢弃之前在因特网中能够生存的最大时间,MSL常用数值为30~60秒



化身

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


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

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


同时打开
双方同时发送SYN,双方都受到SYN后,又都同时回复SYN+ACK,此时连接建立
比较罕见,没有客户端与服务器之分,双方是对等的。

同时关闭
两端都发出主动关闭,双方的TCP都进入FIN_WAIT_1状态,并发送FIN报文段。这两个报文段同时在传送。收到FIN报文段之后,双方都进入CLOSING状态,并发送ACK报文段。CLOSING状态取代了常见场景的FIN_WAIT_2和CLOSE_WAIT状态。收到ACK之后,双方进入TIME_WAIT状态。

拒绝连接
A发送SYN,B回复RST+ACK

异常终止连接

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



6 TCP中的窗口

6.1 发送窗口

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

2:在某些实现中,TCP可以保存从进程接收的数据,并且稍后在发送出去。(即在发送窗口和进程缓冲区之间,还有一个缓存)

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


6.2 接收窗口

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

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

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


7 流量控制



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

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

     就是图上的5,一旦发送串口满了就是拒绝接收数据

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

7.1 打开和关闭窗口

       为实现流量控制,TCP强制发送方和接收方不断调整它们窗口的大小。当有更多字节从发送方传过来时,其接收窗口关闭(左壁向右移动),二挡更多字节被进程拉取走时,其发送窗口关闭(右壁向右移动),假设接收窗口不会收缩(右壁不向左移动)。

       发送窗口的打开,关闭、收缩实收接收方控制的。当一个新的确认到达时,且这个确认允许时,其发送窗口关闭(左壁向右移动)。当接收方通告窗口大小(rwnd)允许时,其接受窗口打开(右壁向右移动)。

7.2 窗口的收缩

      接收方指定rwnd值可能会导致发送窗口的缩小,此时发送窗口右壁向左收缩,有些实现强制发送窗口不能收缩(只是右壁不能向左移动),

限定:新的ackNO+新的rwnd >= 最近的ackNO+最近的rwnd

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

窗口关闭

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

7.3 糊涂窗口综合征

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

发送方解决方案:Nagle

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

接收方产生原因

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

接收方解决方案

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

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

8 差错控制

TCP使用差错控制来提供可靠性,机制有:检测和重传收到损伤的报文段,重传丢失的报文段,保存失序到达的报文段直到缺失的报文段到齐,检测和丢弃重复的报文段。使用的工具有:检验和,确认和超时。

8.1 检验和

16位检验和用来检测报文段是否收到损伤,检验不过就有终点TCP丢弃,并认为丢失了。

8.2 确认

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

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

3:确认类型:

(1):累积确认ACK

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

产生确认

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

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+自己期望的下一个序号

8.3 重传

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

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

8.4 失序的报文段

数据可能不按次序到达,接收方TCP会吧它们暂时保存下来,不会提交失序的报文段给进程。

8.5 TCP数据传输的FSM

正常情况

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


报文段丢失


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



快重传

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


延迟的报文段

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

重复的报文段

同延迟的报文段基本一样,丢弃后到的报文段。此时需要发送一个ack,他的ackNO指向所期望的报文段。

自动纠正丢失的ACK

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




因确认丢失而产生的死锁

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

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

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

9 拥塞控制

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

9.1:拥塞窗口

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

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

9.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:启动拥塞避免阶段


10 TCP的计时器

10.1RTO重传计时器

1)当TCP发送了位于发送队列中第一个报文段时,启动此计时器,

2)当这个计时器超时后,TCP重传位于发送队列第一个未被确认的报文段,并重启计时器。

3)当一个(或多个)报文段被累积确认后,这个(或这些)报文段被消除出队列。

4)当一个队列为空,则TCP停止此计时器,否则重启定时器

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

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

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

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

10.2 持续计时器

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

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

10.3 保活计时器

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

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

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

10.4 TIME_WAIT计时器

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

11 选项

选项结束(EOP):用来在选项区结尾处进行填充。zhi允许出现一次。
无操作(NOP):用作选项间的填充,可多次使用。

最大报文段长度MSS

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

窗口扩大因子

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

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

时间戳

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

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

1:测量往返时间RTT:

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

2:防止序号绕回PAWS:

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

允许SACK和SACK选项:

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

SACK选项先是宣布重复的数据,再是失序的块。

12 TCP软件包

包含了几张传输控制块的表,一组计时器,主模块,输入模块,输出模块。

12.1 传输控制模块

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

TCB包含的字段包括:

状态:连接此时的状态

进程:使用该连接的进程

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

本地窗口,远程窗口

发送序号,接收序号

已发送ACK号

往返时间:保存多个RTT

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

缓存大小

缓存指针

12.2 计时器

前面已经介绍过

后面的模块,作者说本书属于入门教材不多展开。

****************************************************************************

原创粉丝点击