TCP如何保证可靠传输

一:TCP如何保证传输可靠性。

                                          

TCP通过序列号(排序和删除重复的数据也是经过序列号来完成的)检验和确认应答信号重发控制连接管理窗口控制流量控制拥塞控制实现可靠性。

 

(1)序列号和确认应答信号(ACK)

序列号(32位):TCP传输时将每个字节的数据都进行了编号,这就是序列号。(为了应对延时抵达和排序混乱)。每个连接都会选择一个初始序列号,初始序列号(视为一个32位计数器),会随时间而改变(每4微秒加1)。因此,每一个连接都拥有不同的序列号。

序列号的作用不仅仅是应答的作用,有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据。这也是TCP传输可靠性的保证之一。

确认应答(32)位:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文。这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。

 

(2)校验和

 

 

 

 

(3)连接管理(说白了就是三次挥手,四次握手)

TCP连接状态(各个状态代表什么要铭记于心)

  • CLOSED:初始状态。
  • LISTEN:服务器处于监听状态。
  • SYN_SEND:客户端socket执行CONNECT连接,发送SYN包,进入此状态。
  • SYN_RECV:服务端收到SYN包并发送服务端SYN包,进入此状态。
  • ESTABLISH:表示连接建立。客户端发送了最后一个ACK包后进入此状态,服务端接收到ACK包后进入此状态。
  • FIN_WAIT_1:终止连接的一方(通常是客户机)发送了FIN报文后进入。等待对方FIN。
  • CLOSE_WAIT:(假设服务器)接收到客户机FIN包之后等待关闭的阶段。在接收到对方的FIN包之后,自然是需要立即回复ACK包的,表示已经知道断开请求。但是本方是否立即断开连接(发送FIN包)取决于是否还有数据需要发送给客户端,若有,则在发送FIN包之前均为此状态。
  • FIN_WAIT_2:此时是半连接状态,即有一方要求关闭连接,等待另一方关闭。客户端接收到服务器的ACK包,但并没有立即接收到服务端的FIN包,进入FIN_WAIT_2状态。
  • LAST_ACK:服务端发动最后的FIN包,等待最后的客户端ACK响应,进入此状态。
  • TIME_WAIT:客户端收到服务端的FIN包,并立即发出ACK包做最后的确认,在此之后的2MSL时间称为TIME_WAIT状态。

 

TCP报文首部(如何确定一个TCP报文的长度?:通过IP首部确定,每个IP包的首部都会标注数据长度)

                              

 

  • 紧急URG,当URG=1,表明紧急指针字段有效。告诉系统此报文段中有紧急数据;
  • 确认ACK,仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1;
  • 推送PSH,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1;
  • 复位RST,当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接;
  • 同步SYN,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1;
  • 终止FIN,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;

来自 <https://www.cnblogs.com/fantastic123/p/8968132.html>

 

  • NOP(NO-Operation):它要求选项部分中的每种选项长度必须是4字节的倍数,不足的则用NOP填充。同时也可以用来分割不同的选项字段。如窗口扩大选项和SACK之间用NOP隔开。
  • 用户超时选项:规则1:当TCP连接达到3次重传阀值时应该通知应用程序。。规则2:当超时大于100秒时应该关闭连接。
  • 时间戳选项(Timestamps):可以用来计算RTT(往返时间),发送方发送TCP报文时,把当前的时间值放入时间戳字段,接收方收到后发送确认报文时,把这个时间戳字段的值复制到确认报文中,当发送方收到确认报文后即可计算出RTT。也可以用来防止回绕序号PAWS,也可以说可以用来区分相同序列号的不同报文。因为序列号用32为表示,每2^32个序列号就会产生回绕,那么使用时间戳字段就很容易区分相同序列号的不同报文。
  • SACK选择确认项(Selective Acknowledgements):用来确保只重传缺少的报文段,而不是重传所有报文段。比如主机A发送报文段1、2、3,而主机B仅收到报文段1、3。那么此时就需要使用SACK选项来告诉发送方只发送丢失的数据。那么又如何指明丢失了哪些报文段呢?使用SACK需要两个功能字节。一个表示要使用SACK选项,另一个指明这个选项占用多少字节。描述丢失的报文段2,是通过描述它的左右边界报文段1、3来完成的。而这个1、3实际上是表示序列号,所以描述一个丢失的报文段需要64位即8个字节的空间。那么可以推算整个选项字段最多描述(40-2)/8=4个丢失的报文段。SACK信息保存于SACK选项中,包含了接收方已经成功接收的数据块的序列号范围。每一个范围被称作一个SACK块,由一对32位的序列号表示。因此,一个SACK选项包含了n个SACK块,长度为(8n+2)个字节,增加的两个字节用于保存SACK选项的种类和长度。
  • 窗口扩大选项(窗口缩放)(Windows Scaling):由于TCP首部的窗口大小字段长度是16位,所以其表示的最大数是65535。但是随着时延和带宽比较大的通信产生(如卫星通信),需要更大的窗口来满足性能和吞吐率,所以产生了这个窗口扩大选项。可将TCP窗口从16为增加到30位。2^30-1刚好是1GB。
  • MSS最大报文段长度(Maxium Segment Size):指明数据字段的最大长度,数据字段的长度加上TCP首部的长度才等于整个TCP报文段的长度。即最大段大小只记录TCP数据的字节数而不包括其他相关的TCP和IP头部。MSS值指示自己期望对方发送TCP报文段时那个数据字段的长度。通信双方可以有不同的MSS值。如果未填写,默认采用536字节,这样正好组成一个576的IPv4数据报。最大段大小的典型值为1460(1500-20-20=1460)。MSS只出现在SYN报文中。即:MSS出现在SYN=1的报文段中
  • 窗口,占2字节,指的是通知接收方,发送本报文你需要有多大的空间来接受;
  • 检验和,占2字节,校验首部和数据这两部分;
  • 紧急指针,占2字节,指出本报文段中的紧急数据的字节数;
  • 选项,长度可变,定义一些其他的可选的参数,如:
  • 源端口和目的端口,各占2个字节,分别写入源端口和目的端口;
  • 序号(seq),占4个字节(32位),TCP连接中传送的字节流中的每个字节都按顺序编号。例如,一段报文的序号字段值是 301 ,而携带的数据共有100字段,显然下一个报文段(如果还有的话)的数据序号应该从401开始;序号被系统初始化为某个随机值ISN(初始序列号)。
  • 确认号(ack),占4个字节(32位),是期望收到对方下一个报文的第一个数据字节的序号。例如,B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确的收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701;
  • 数据偏移(首部长度),占4位,由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报头不包含任何任选字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,15*32/8 = 60,故报头最大长度为60字节(首部长度范围:20-60字节)。首部长度也叫数据偏移,是因为首部长度实际上指示了数据区在报文段中的起始偏移值
  • 保留,占6位,保留今后使用,但目前应都位0;

 

 

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

 

         

 

 

  1. TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态
  2. TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
  3. TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
  4. TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
  5. 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。

TCP连接的释放(四次挥手)

             

 

  1. 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
  2. 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
  3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)
  4. 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
  5. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
  6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。

 

 

同时打开和同时关闭时,需要交换4个报文段,比普通的三次握手增加了一个。

防止进入FIN_WAIT_2状态:

如果负责主动关闭的应用程序执行的是一个完全关闭操作,而不是用一个半关闭来指明它还期望接受数据,那么就会设置一个计时器。如果当计时器超时时连接是空闲的,那么TCP连接就会转移到CLOSED状态。这个时间的默认值是60s   

 

问题:

1.为什么TCP客户端最后还要发送一次确认呢?(即为什么是三次握手而不是两次握手)

 

一句话, 主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误 。(判断是否请求连接)
 
如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于 TCP 的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
 
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

 

       

 

2.为什么客户端最后还要等待2MSL?(2个点)

 

MSL Maximum Segment Lifetime ), TCP 允许不同的实现可以设置不同的 MSL 值。
第一, 保证客户端发送的最后一个 ACK 报文能够到达服务器,因为这个 ACK 报文可能丢失, 站在服务器的角度看来,我已经发送了 FIN+ACK 报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次 ,而客户端就能在这个 2MSL 时间段内收到这个重传的报文,接着给出回应报文,并且会重启 2MSL 计时器。
第二 ,防止类似与 三次握手 中提到了的 已经失效的连接请求报文段 出现在本连接中。客户端发送完最后一个确认报文后, 在这个 2MSL 时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失 。这样新的连接中不会出现旧连接的请求报文。
 

 

为什么建立连接是三次握手,关闭连接确是四次挥手呢?
建立连接的时候, 服务器在 LISTEN 状态下,收到建立连接请求的 SYN 报文后, ACK SYN 放在一个报文里发送给客户端
而关闭连接时,服务器收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送 FIN 报文给对方来表示同意现在关闭连接,因此, 己方 ACK FIN 一般都会分开发送 ,从而导致多了一次。

 

3.如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP 还设有一个 保活计时器 ,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器, 时间通常是设置为 2 小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段 ,以后每隔 75 秒钟发送一次。若一连发送 10 探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接

        

 

1)超时与重传

超时重传的几种情况:

(1)数据报中途丢失

(2)数据报顺利到达对端,但ACK报文中途丢失

(3)数据报顺利达到对端,但对端异常未响应ACK或者被对端丢弃。

出现以上异常情况就会超时重传:在发送一个数据之后,就开启一个定时器,若是在这个时间内没有收到

发送数据的ACK确认报文,则对该报文进行重传,在达到一定次数还没有成功时放弃并发送一个复位信号。

RTO的调整

 

相关定义

超时重传:当 TCP 在发送数据时,数据ack 都有可能会丢失,因此,TCP 通过在发送时设置一个重传计时器来解决这种问题。如果重传计时器溢出还没有收到确认,它就重传数据。

超时重传时间(RTO重传计时器所设定的时间。

往返时间(Round-Trip Time, RTT)从发送方发送数据开始,到发送方收到来自接收方的确认(接受方收到数据后便立即发送确认)总共经历的时间。

 

TCP片段重传计时器&重传队列

检测丢失片段并对之重传的方法概念上是很简单的。每一次发送一个片段(一个TCP数据段),就开启一个重传计时器。计时器有一个初始值并随时间递减。如果在片段接收到确认之前计时器超时,就重传片段。TCP使用了这一基本技术,但实现方式稍有不同。原因在于为了提高效率需要一次处理多个未被确认的片段,以保证每一个在恰当的时间重传。

 

工作顺序

1.放置于重传队列中,计时器开始

包含数据的片段一经发送,片段的一份复制就放在名为重传队列的数据结构中,此时启动重传计时器。因此,在某些时间点,每一个片段都会放在队列里。队列按照重传计时器的剩余时间来排列,这样可追踪哪几个计时器将在最短时间内超时。

 

2.确认处理

如果在计时器超时之前收到了确认信息,则该片段从重传队列中移除。

 

3.重传超时

如果在计时器超时之前没有收到确认信息,则发生重传超时,片段自动重传。当然,相比于原片段,对于重传片段并没有更多的保障机制。因此,重传之后该片段还是保留在重传队列里。重传计时器被重启,重新开始倒计时。如果重传之后没有收到确认,则片段会再次重传并重复这一过程。在某些情况下重传也会失败。我们不想要TCP永远重传下去,因此TCP只会重传一定数量的次数(几次?:第一次发送后所设置的超时时间实际上为1.5此后该时间在每次重传时增加一倍一直到64,采用的是指数退避算法。一共重传12,大约9分钟才放弃重传,该时间在目前的TCP实现中是不可变的,Solaris2.2允许管理者改变这个时间,tcp_ip_abort_interval变量。且其默认值为两分钟,而不是最常用的9分钟。),并判断出现故障终止连接。

 

但是我们怎样知道一个片段被完全确认呢?重传是基于片段的,而TCP确认信息是基于序列号累积的。每次当设备A发送片段给设备B,设备B查看该片段的确认号字段。所有低于该字段的序列号都已经被设备A接收了。因此,当片段中所发送的所有字节的序列号都比设备A到设备B的最后一个确认号小的时候,一个从设备B发到设备A的片段被认为是确认了。这是通过计算片段中最后一个序列号结合片段的数据字段来实现的。

 

举例

让我们以下图为例来说明一下确认和重传是怎样工作的。假设连接中的服务器发出了四个连续片段(号码从1开始)

片段1 序列号字段是1片段长度80。所以片段1中最后一个序列号是80。

片段2 序列号是81片段长度是120。片段2中最后一个序列号是200。

片段3 序列号是201片段长度是160。片段3中最后一个序列号是360。

片段4 序列号是361片段长度是140。片段3中最后一个序列号是500。

这些片段是一个接一个发送的,而无需等待前一个发送得到确认。这是TCP滑动窗口的一个主要优势(细说TCP滑动窗口)。

 

假设客户端接收到前两个传输,它会发回一条确认消息确认号为201。从而告知服务器前两个片段已经被客户端成功接收了,它们从重传队列中移除(并且服务器发送窗口右移200字节)。在接收到确认号361或更高的片段之前,片段3会保留在重传队列中;片段4需要确认号501或更高。

 

现在,让我们进一步假设传输过程中片段3丢失了,但片段4被接收到了。客户端将片段4保存在接收buffer中,但是不需要确认,因为TCP是累积确认机制——确认片段4表示片段3也接收到了,但实际上并没有。因此,客户端需要等待片段3。实际上,服务器端片段3的重传计时器会超时,服务器之后重传片段3。之后客户端收到,然后发送片段3和4的确认信息给服务器。

 

还有一个重要的问题,服务器将如何处理片段4呢?虽然客户端在等待片段3,服务器没有收到反馈,所以它并不知道片段3丢失了,同样它也不知道片段4发生了什么(以及接下来传输的数据)。很有可能客户端已经接收到了片段4但是不能确认,也有可能片段4也丢失了。一些实现中会选择仅仅重传片段3,也有些会把3和4都重传。

 

链接地址:

来自 <https://community.emc.com/message/842129#842129>

来自 <https://blog.csdn.net/q1007729991/article/details/70196099>

 

 

 

2TCP协议中的计时器

 

(1)重传计时器

 

(2)持续计时器(坚持计时器)

 

(3)保活计时器

 

(4)时间等待计时器

 

 

3)流量控制和滑动窗口

https://www.cnblogs.com/woaiyy/p/3554182.html

猜你喜欢

转载自blog.csdn.net/ypshowm/article/details/89173791