TCP如何保证可靠传输
一:TCP如何保证传输可靠性。
TCP通过***(排序和删除重复的数据也是经过***来完成的)、检验和、确认应答信号、重发控制、连接管理、窗口控制、流量控制、拥塞控制实现可靠性。
(1)***和确认应答信号(ACK)
***(32位):TCP传输时将每个字节的数据都进行了编号,这就是***。(为了应对延时抵达和排序混乱)。每个连接都会选择一个初始***,初始***(视为一个32位计数器),会随时间而改变(每4微秒加1)。因此,每一个连接都拥有不同的***。
***的作用不仅仅是应答的作用,有了***能够将接收到的数据根据***排序,并且去掉重复***的数据。这也是TCP传输可靠性的保证之一。
确认应答(32)位:TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文。这个ACK报文当中带有对应的确认***,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。
(2)校验和
(3)连接管理(说白了就是三次挥手,四次握手)
TCP连接状态(各个状态代表什么要铭记于心)
|
TCP报文首部(如何确定一个TCP报文的长度?:通过IP首部确定,每个IP包的首部都会标注数据长度)
|
TCP连接的建立(三次握手)
- TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
- TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始*** seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
- TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个*** seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
- TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的***seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
- 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。
TCP连接的释放(四次挥手)
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其***为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的***seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的***为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的***是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入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.****.net/q1007729991/article/details/70196099>
(2)TCP协议中的计时器
(1)重传计时器
(2)持续计时器(坚持计时器)
(3)保活计时器
(4)时间等待计时器
(3)流量控制和滑动窗口
https://www.cnblogs.com/woaiyy/p/3554182.html