菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
303
0

TCP协议

原创
05/13 14:22
阅读数 92532

1 TCP协议

1.1 TCP认识

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接、可靠的、基于字节流的传输层协议。

TCP在传送数据之前会先相互发送一些预备报文段协商一些參数,比方序号等等。TCP将用户数据打包成报文段。发送数据后启动一个定时器,还有一端对收到的数据进行确认。对失序的数据又一次排序,丢弃反复数据,TCP提供端到端的流量控制,并计算和验证一个强制性的端到端校验和。在OSI七层模型中位于第四层,在TCP/IP四层模型中位于第三层。
使用TCP的应用程序:Telnet、Rlogin、FTP、SMTP

1.2 TCP可靠性保证方式

(1)应用数据被切割成TCP觉得最适合发送的数据块。这和UDP全然不同。应用程序产生的数据报长度将保持不变。

由TCP传递给IP的信息单位称为报文段或段(segment)
(2)当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。

如果不能及时收到一个确认,将重发这个报文 段。
(3)当TCP收到发自TCP连接还有一端的数据,它将发送一个确认。这个确认不是马上发送,通常将推迟几分之中的一个秒
(4)TCP将保持它首部和数据的检验和。

这是一个端到端的检验和,目的是检測数据在传输过程中的不论什么变化。

如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段(希望发端超时并重发)。
(5)既然TCP报文段作为IP数据报来传输,而I P数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。

如果必要。TCP将对收到的数据进行又一次排序,将收到的数据以正确的顺序交给应用层。
(6)既然IP数据报会发生反复。TCP的接收端必须丢弃反复的数据
(7)TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。

TCP的接收端仅仅同意还有一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。

2 TCP连接的建立与断开

2.1 TCP三次握手和四次挥手示意图

TCP建立连接和断开连接示意图

2.2 TCP的11种转态

TCP状态变迁图
(1)Client独有:SYS_SENT, FIN_WAIT1, FIN_WAIT2, TIME_WAIT, CLOSING
(2)Sever独有:LINSTEN, SYN_RECD, CLOSE_WAIT, LAST_ACK
(3)共同拥有:CLOSED, ESTABLISHED
当client处于FIN_WAIT_1状态时,若同一时候接收到FIN,ACK能够不经过FIN_WAIT_2状态直接进入TIME_WAIT状态

2.3 TCP建立连接(三次握手)

(1)client、server端初始转态CLOSED, server端创建SOCKET開始监听,server端转态变为LINSTEN,client发送SYN进行建立连接的请求,client转态变为SYS_SENT
(2)服务端收到client发来的SYN后,向client发送ACK确认及SYN,server端转态变为SYN_RECD
(3)client收到server端发来的ACK及SYN后。向server端发送确认ACK,client转态变为ESTABLISHED
(4)server端收到client发来的ACK后。转态变为ESTABLISHED,建立连接完成

TCP建立连接须要三次握手的原因:
(1)防止已经过期的连接再次传送到被连接的主机
client端向server发送第一个连接请求报文段并没有丢失而是在网络中某个节点滞留到client和server连接结束后的某个时间点到达了server,若不採用三次握手。server发送ACK此时server端觉得连接就已经建立,可是client端并没有发送连接请求因此对server发来的ACK不予理睬,这样server就一直等到client发送数据,浪费了server端的资源。
(2)防止出现死锁
A向B请求连接,B收到后发出确认。B觉得连接成功,開始向A发送数据,可是B发往A的确认丢失了,A觉得连接还未成功建立会忽略B发来的其它不论什么消息仅仅等待连接确认,A在发送数据(非确认)超时时反复发送同样分组。这就形成了死锁。

2.4 TCP结束连接(四次挥手)

(1)此时client和server端转态都为ESTABLISHED。client传输数据完成请求断开连接,client发送FIN给server端,client转态变为FIN_WAIT1
(2)server端收到client发来的FIN后对其进行确认发送ACK,server端转态变为CLOSE_WAIT
(3)client收到server端发来的ACK后转态变为FIN_WAIT2,此时client到server端的连接关闭,可是server到client的连接还么有关闭,server还能够向client发送数据
(4)当server端没有数据要给client发送时。服务端向client发送FIN,server端转态变为LAST_ACK
(5)client收到server端的FIN后向其发送ACK确认。client进入TIME_WAIT转态,client等待2MSL时间后进入CLOSED转态
(6)server端收到client的ACK后。转态变为CLOSED,此时TCP连接全然关闭

以上是TCP断开连接不出错的情况下的步骤,当中还有一种client的转态CLOSING没有提到,这样的情况发生在client向server端发送FIN后。client没有接收到server发来的ACK却接收到server发来的FIN时,client转态从FIN_WATI1直接进入CLOSING转态,当接收到server的ACK后再从CLOSING转态进入TIME_WAIT转态。

TCP结束连接须要四次握手原因:
当client没有数据向server端传输时发送FIN要求结束client到server端的连接,但此时可能server端还有数据向client发送因此,server端仅向client发送ACK(并没有一起发送ACK与FIN)结束client到服务端的连接。可是此时server端到client的连接还是有效的。当server端没有数据向client发送时server端再发送FIN,然后clientACK对其确认。

结束连接时从TIME_WAIT到CLOSED等待2MSL的原因:
(1)由于收到server发来的FINclient发送ACK确认,可是ACK可能丢失。在client收到server端发来的FIN(在CLOSING转态下接收到的是ACK)后直接进入CLOSED这样的情况下,若client发给server端的ACK丢失,server端没有接收到ACK超时就重发FIN,可是client已经关闭,server端就会不断超时重发FIN,无法关闭server端到client的连接。若设置了TIME_WAIT转态,client发送ACK后等待2MSL时间。若此时间内再次收到server端的FIN。client就重发ACK,并从新等待2MSL;若此时间内没有再次收到server端的FIN,client就猜測server端已经接收到ACK,连接已经断开,进入CLOSED转态。

(MSL(Maximum Segment Lifetime)指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间)
(2)TCP连接在2MSL等待期间。定义这个连接的插口(客户的IP地址和port。server的IP地址和port)不能再被使用,这个连接仅仅能在2MSL结束后才干再被使用。

在连接处于2MSL等待期间,不论什么迟到的报文段将被丢弃,这就防止了将他觉得为使用同样插口的新的连接的一部分。

TCP存在半关闭的原因:
client向server端发送FIN请求关闭client端server端的连接,server端向client发送ACK确认,此时client处于FIN_WAIT2状态。server端处于CLOSE_WAIT状态。此时称为半关闭状态。

比方client向server端发送数据,要在server端进行排序,server端必须所有接收到要排序的数据后才干够開始进行排序工作。那么server端是怎样知道client发送数据完成了呢?就是通过client向server端发送FIN请求关闭client到server端的连接知道数据已经接收完成了。開始排序并把排序后的数据从server端发送到client。

(当然也能够建立两个TCP连接,一个用于client向server端发送数据,一个用于server端向client发送数据。但这个消耗大费时)

3 TCP报文首部格式

3.1 TCP报文首部格式图

TCP报文首部图

3.2 TCP报文首部各字段

(1)16位源port号:标记TCP报文段来自上层那个port(用于寻找响应的应用进程)。多路复用来自上层应用的数据。


(2)16位目的port号:标记TCP报文段应该送至上层那个port(用于寻找响应的应用进程),多路分用送至上层应用。


(3)32位序号:发送方发送数据报文段的序号(首字节序号)。比方第一个报文段的序号是0。有500个字节(0-499)。那么下一个报文段的序号就是500,长度为500字节(500-999),第三个报文段序号为1000,长度500字节(1000-1499)。
(4)32位确认序号:对已经接收的连续的数据进行确认。比方接收方顺利接收到(3)中的第一个报文段则接收方向发送方发送的确认序号为500,表示500曾经的所有数据都已正确接收,下次期待接收序号500的数据;若是先接收到第一个报文段然后接收到第三个报文段。可是第二个报文段没有接收到此时接收方会反复发送确认序号500(累积确认)。这样的情况下有两种处理方式,一是把失序的第三个报文段丢弃,而是保持第三个报文段等待第二个报文段到来填充间隔。
(5)4位首部长度:表示以32bit的字为单位的TCP首部长度。由于有“选项”字段因此TCP首部长度是可变的。可是“选项”字段通常为空。


(6)保留(6位):保留未用字段6bit
(7)URG:紧急字段1bit,置为1 表示报文段中有被上层置为“紧急”的数据,后面第15字段“16位紧急指针”有效且指出了报文段中“紧急”数据的最后一个字节。

使用时置为1。用来处理避免TCP数据流中断。(实际中未被使用)
(8)ACK:置为1表明“32位确认序号”字段是有效的。
(9)PSH:置为1时表示接收方尽快将这个报文段交给应用层。接收方接收到数据后不必等到数据缓冲区满后才传送给应用程序而是接收后直接传送给应用程序。(实际中未使用)
(10)RST:用于复位由于主机崩溃或其它原因出现的错误连接,也能够用于拒绝错误的数据报和非法连接,常见的一种情况是当连接请求到达时目的port没有进程监听。TCP产生RST。也可异常终止一个连接而不是使用FIN,RST也能够用于检測半打开连接。


(11)SYN:同步序号,用于TCP连接的建立(參考TCP连接图示)
(12)FIN:用于断开TCP连接(參考TCP断开连接图示)
(13)16位窗体大小:窗体大小为字节数,表明接收端期望接收的字节数,TCP连接两端的窗体大小用于进行TCP流量控制。


(14)16位校验和:TCP校验和覆盖整个TCP报文段:TCP首部和TCP数据部分,由发送端计算存储。由接收端验证。(使用到伪首部)
(15)16位紧急指针:URG位置为1时有效。紧急指针是一个正偏移量,与32位序号字段相加获得紧急数据最后一个字节的序号。
(16)选项:一般TCP连接的两端在通信的第一个报文段中在这个字段指出本端能接收的最大报文段大小MSS(Maximum Segment Size),一般这个字段为空。


(17)数据:可选,在连接建立和终止时这个字段一般为空,仅有TCP首部。

4 最大报文段(MSS)

TCP在建立是会在SYN报文(且仅仅会出如今SYN报文中)中告知对方自己的最大报文段。

这样两方在发送数据时发送的报文段通常会小于两方最大报文段的最小值,这样是为了尽量避免报文在传输过程中分段。若一方没有设置MSS值则採用默认值536字节(再加上20字节的TCP首部,20字节的IP首部能够得到最大576字节的IP数据报)。

MSS值一般设置为MTU值减去20字节TCP首部再减去20字节IP首部。这样并不能全然避免分段,比方主机A,B都连接到以太网上最大MSS都使用536字节或1460字节,可是中间网络有的最大MTU为296字节,那么A,B发送的数据在经过这部分网络时还是会分段的。

下图说明了A,B主机怎样告知对方自己同意的MSS,和网络中在哪分段:
TCP_MSS

5 拥塞避免算法

拥塞避免算法是用于处理丢失分组的方法。一般在网络上由于分组损坏而丢失分组的概率非常小,因此一般觉得分组丢失是由于源主机到目的主机之间的某处网络发生了拥塞,观察拥塞的方式有两种:一是超时,二是连续接收到3个反复ACK。

为什么是3个连续的ACK:由于我们不知道一个反复的ACK是由一个丢失的报文引起的,还是由于仅仅出现了几个报文段的又一次排序引起的。因此我们等待少量的反复的ACK到来。由于若仅仅是一些报文段的又一次排序引起的。一般在又一次排序报文段完成并产生一个的ACK之前仅仅可能产生1-2个反复的ACK。

拥塞的不同处理方式:对于由于超时重传觉得的拥塞,我们通常是重传报文段,然后进入慢启动(下文5.1)。对于由于接收到3个反复的ACK觉得的拥塞。我们通常是马上重传报文段,然后进入拥塞避免(下文5.2)。这样处理的原因是由于当由于定时器超时。此时网络中可能已经非常拥塞,数据确认的ACK已经无法发送回来,因此我们马上降低注入网络中的数据。使用慢启动cwnd减小为1。而对于收到,3个反复的ACK说明还有其它的报文段到达了目的地(由于接收方仅仅有在收到失序的报文段时才会产生反复的ACK并且还有反复的ACK发送回来),也即收发两端还有数据的流动,因此我们不必使用慢启动突然降低注入网络的数据。

注:(1)下面为了便于讨论如果接收方的接收窗体足够大,实际中发送窗体取接收窗体和拥塞窗体的最小值。因如果接收窗体足够大,因此发送窗体就等于拥塞窗体(cwnd)。设慢启动门限为ssthresh。(2)为了便于讨论窗体大小的单位都是用报文段(实际中是以字节为单位)。

5.1 慢启动算法与拥塞避免算法

TCP慢启动
(1)初始化cwnd=1, ssthresh=16,開始慢启动
(2)0-4往返时间内cwnd < ssthresh运行的是慢启动算法,cwnd以指数的方式添加,每接收一个ACK,cwnd就添加一个报文段大小。

第一个RTT内发送一个报文段。接收到一个ACK,cwnd变为2,第二个RTT发送2个报文段,接收两个ACK。cwnd变为4,以此类推….
(3)当cwnd==ssthresh时既能够运行慢启动也能够运行拥塞避免
(4)当cwnd>ssthresh时開始运行拥塞避免算法,cwnd呈线性增长,每收到一个ACK,cwnd就添加1/cwnd大小。比方4-5RTT。发送了16个报文段,每接收一个ACK,cwnd就添加1/16,16个接收完也即一个传输轮次(RTT)cwnd添加1。4-12都如此运行。
(5)若在12-13时。发生了超时重传分组,就把ssthresh设置为此时cwnd的一半大小,cwnd大小又一次设为1。再次開始运行慢启动,如上图。

5.2 快重传和快恢复

TCP快重传快恢复
当因收到三个及三个以上的反复ACK时。使用例如以下拥塞控制方法:
(1)收到3个反复的ACK。将ssthresh值设为当前cwnd窗体大小的一半,以图为例在cwnd=24时收到三个反复的ACK。把ssthresh设为12。
(2)此时马上重传丢失的报文不用等到定时器超时,此为快重传。


(3)此后開始运行拥塞避免而不是慢启动,此为快恢复。

注:在TCP的详细的实现中在运行”乘法减小”的过程中可能还有一些隐含的操作(不同版本号实现可能不同样,一般的教科书讲述的会像是以上两幅图描写叙述的那样)。当收到3个反复的ACK时,把ssthresh设置为当前cwnd值得一般,把cwnd值设为ssthresh+3(由于收到三个反复的ACK说明有三个报文段离开了网络),若再收到反复的ACK,cwnd就再加1,直到收到新数据的ACK,此时把cwnd设置为ssthresh(或ssthresh+1),然后開始运行拥塞避免。

(在收到反复的ACK期间继续发不发新的报文段。要比較cwnd的值和未被确认的数据的值)。下面是《TCP详细解释:卷一》第21章中的一幅图。说明了上图连续收到三个反复ACK时cwnd和ssthresh的详细变化(图中单位都是字节。报文段大小256字节,在接收到第三个反复ACK时当前窗体大小为2426字节,取其一半然后四舍五入到报文段整数倍大小即为1024赋值给ssthresh,注意这里实际的实现和理论的差异哦,不要太刻板哦):
这里写图片描写叙述

5.3 总结

对TCP两种检測到丢包的方式(超时和三次反复ACK)产生的对应操作总结例如以下图:
这里写图片描写叙述

6 TCP处理ICMP差错

6.1 源站抑制

对于这样的ICMP差错,设置拥塞窗体cwnd为1,发起慢启动。可是慢启动的门限ssthresh不变化。

6.2 主机不可达或网络不可达

对于这两种ICMP差错。都能够忽略,由于这两种差错被觉得是短暂现象,有可能是由于中间路由器关闭而导致选路协议要花费数分钟才干稳定到还有一个替换路由。TCP还是会试图发送引起差错的数据,终于数据可能发送成功,也可能多次重传超过重传最大次数而失败。

7 又一次分组

TCP超时并重传时,并不一定重传同样的报文段。TCP同意又一次分组发送一个较大的报文段,如:我们首先键入第一行”first line”发送成功。然后断开网络,键入第二行”second line”发送,此时发送失败,我们在又一次发送第二行之前键入第三行”third line”。然后接上网络,这样TCP就有可能把第二行数据和第三行数据放在同一个报文段中发送。

8 TCP定时器

8.1 重传定时器

原因:TCP是提供的是可靠的传输。它通过对发送的报文段进行确认来保证,若发送一个报文后在规定的时间内没有收到对应的确认,就须要重传报文段,超时时间的设定就是通过重传定时器实现的。


工作方法:发送一个报文段在重传定时器规定的时间内没有收到对应的确认,就又一次发送这个报文段。
算法:使用指数退避算法进行重传。比方第一次1.5s重传。若还没有收到对应的确认第二次3s重传,第三次6s重传,依次类推。直到超时重传的上限,在上限可能尝试多次。若还是发送不成功则放弃。

8.2 persist定时器

原因:当接收方通告其接收窗体为0时,发送方停止发送,然后接收方发送更新窗体报文通告发送方,若窗体更新报文丢失,那么接收方觉得成功一直等待发送方发送数据,而发送方一直等待接收方的更新窗体报文,这就形成了死锁,这就须要一个persist定时器使发送方能够周期性的询问接收方的窗体大小,破除可能的死锁状态。
工作方法:当发送方收到一个通告接收窗体为0的报文时就触发发送方的persist定时器,然后发送方若在一定时间内没有
接收到对应的窗体更新报文,就在规定的时间间隔到来时发送一个报文段(窗体探測报文window probe)询问接收方的窗体大小,接收方对这个报文确认并给出窗体大小。


算法:採用相似指数退避的算法来的发送窗体探測报文,和超时重传不同的一点是TCP从不放弃发送window probe报文,
直到窗体打开或连接被终止。

8.3 2MSL定时器

原因:当client收到server的FIN后发送ACK,可是这个ACK可能在传输中丢失,因此server方可能超时重发FIN,若client直接进入CLOSED状态那么就无法处理server重发的FIN,那么server就无法正常关闭。
工作方法:当client收到server发来的FIN后发送ACK。然后client进入2MSL的TIME_WAIT状态,若在2MSL时间内没有再收到server的FIN,client就如果server已经正确接收了ACK已经关闭。client也转为CLOSED状态;若在2MSL内再次接收到server端的FIN。那么client就重发ACK,并把定时器又一次设2MSL。
算法:每重发一次就又一次把定时器设置为2MSL,从新等待2MSL时间。
注:以上也可能server重发的FIN丢失或被延迟。等server发的FIN到达client时定时器已经到2MSL,client进入CLOSED状态了。这样的情况下server没有正常关闭,但这样的情况极少发生。我们可使用下一个保活定时器处理这样的情况。

8.4 保活定时器

原因:检測空暇连接的还有一端何时停止或重新启动。
工作方法:给定的连接在规定的时间内(一般2小时)没有不论什么动作,则server向client发送一个探查报文段,当client处于不同情况有不同的处理方法。

下面分四种讨论:
(1)client依旧正常运行,并从server可达。

server会在两小时后把保活定时器复位,若在两小时之内有数据交互。那么保活定时器在交互数据后的两小时再复位。
(2)client崩溃,并且关闭或者正在又一次启动。这样server发出的保活报文无法得到client的回复,若探查超时server会发送一定次数的这样的保活报文,若都没有得到应答。则server觉得client已经关闭并终止连接。
(3)client崩溃并已经又一次启动。此时server能收到client的响应,可是client刚刚又一次启动不认识这个连接,因此响应是一个复位报文。使得server终止这个连接。
(4)client端正常。可是不可达。同样是不能得到应答,server无法区分(2)与(4)。


算法:针对不同情况已在工作方法中提到。
不主张使用保活(keepalive)定时器的理由:
(1)在出现短暂差错情况下。可能释放掉一个非常好的连接(比方两端中间网络出现暂时故障,如中间路由崩溃并重新启动,若此时发送了保活探查一端会觉得还有一端已经崩溃)
(2)消耗不必要的带宽
(3)若是按分组收费。在互联网上的费用添加

注:无论是client还是server端都能够使用保活功能,可是我们通常是在server端使用,比方NFS两端都使用了此功能,而Telnet和Rlogin仅在server端使用了此功能。

保活定时器应该有运输层提供还是应用层提供说法不一,保活功能主要是为server的应用程序提供的。server应用程序希望知道client是否崩溃,从而能够代表client使用资源。

注:未完待续

发表评论

0/200
303 点赞
0 评论
收藏
为你推荐 换一批