TCP是如何保证可靠数据传输的?

TCP为应用程序提供可靠的通信连接,因为他采用了三次握手协议,三次握手协议指的是在发送数据的准备阶段,服务器端和客户端之间需要进行三次交互。

第一次握手:客户端发送SYN包到服务器,并进行SYN_SEND状态,等待服务器确认;

第二次握手:服务器收到SYN包并确认,同时自己发送一个SYN+ACK包,此时服务器进入SYN_RECV状态

第三次握手:客户端收到服务器的SYN_ACK包,向服务器发送确认包ACK,此包发送完毕,客户端和服务器进入established状态,完成三次握手

连接建立后,客户端和服务器就开始进行安全可靠的数据传输了。

什么是可靠,所谓的可靠就是说发送方发送的数据到达接收方的时候不会发生错误,不会丢失,不会乱序。

在网络层表现看来是这样的,当从运输层传下报文段之后,封装成ip数据报,然后经过复杂的网络传输到目的主机,在传输过程中可能在这个复杂的大网络中发生数据报的分片,丢失的情况。当到达目的主机后,在目的主机的网络层进行对收到的数据报进行拼装,发现这个报文段缺少了一些部分不完整,所以在目的主机的网络层就是吧这个报恩段丢弃,传送给源主机一个ICMP报文段请求源主机重新发送这个报文。当这个报文安全无误的到达目的主机的网络层之后然后去掉首部直接上相交付给运输层的接受缓存中。但是在向上交付报文段的时候可能接受缓存满了导致这个报文段丢弃或者在硬件传出过程中发生了比特差错这也不是不可能的。为了避免数据不可抛传输,那么在运输层就要对数据的可靠性做一个保证。

在运输层分为两个协议UDP和TCP,UDP是一个不可靠的协议,也就是说他仅仅提供复用和分用的功能但是对于比特差错或者丢弃不做任何处理。但是TCP是一个面向连接的协议,他能够保证从源主机交付的数据正确无误的传输到目的主机的对应的进程中。所以接下来将要介绍的是TCP是如何保证数据可靠的传输的。

1.TCP是怎么保证没有比特差错的?

为了保证接受的报文段是没有比特差错的,TCP中引入了这三个机制:

①差错检测:也就是引入校验和。在TCP的首部中有一个占据16为的空间用来放置校验和的结果。在源主机的运输层开始接受到一个从应用进程传下来的数据的时候,会将他封装成一个报文段,加上至少20字节的首部。同时会将这个报文段首部和数据还有伪首部部分一起根据取反码和的形式计算出校验和添加到首部中。传输到目的主机的运输层之后,会计算这个通过这个校验和检查是否存在比特差错。

控制消息:当检测到发生比特差错之后要对发送发进行信息的反馈使得能够根据返回决定是否进行重传。一般理论上会有两种返回一种是肯定的反馈ACK,在TCP中反馈信息是接收到的分许中最后一个字节序号的下一位。一种是否定反馈,但是为了减少网络中的注入分组的数量减少负担取而代之的是通过发送上一个分组的确认信息表明当下这个分组没有正确的接收。

重传:如果接收方接收到的是一个换掉的ACK或者上一个分组的确认之后意味着要再发一遍这个分许。怎么发后面将分析到。

2.TCP是怎么保证重传的?

发送发重传之后,接收方值怎么知道这个分组是重传的分许,或者说接收方怎么知道我是不是已经接受了这个分组的?引入了序列化的机制。由于TCP是面向字节流的协议,所以会为每一个字节编制一个序号。同时在TCP首部中也会有这个序号字段。但是由于一个分组中包含了多个字节,所以说这个TCP首部中的序号是分组中发送多个字节的第一个字节的序号值。

3.TCP是怎么处理分组丢弃的问题的?

以上这几个问题是基于分组没有丢弃但是仅仅发生比特差错的时候。如果发生比特差错怎么办。协议张引入了定时器这个机制。也就是说在某一时刻设置一个定时器,只要在定时器设置的时间内没有收到对应分组的确认信息,那么就会重传对应的一个或者几个分组。

4.TCP是如何提高传输性能的?

在TCP协议中为了提高传输性能是不可能一次只能发送一个分组介绍到这个分组确认之后在发送下一个分组的,因为这样对于信道的利用效率会大大减少,所以为了提高信道的利用率一般一次发送多个分组,并且将这几个分组为单位开始设置定时器。

同时也引入了快速重传的机制。 

5.TCP为什么引入接受缓存这个数据结构

如果没有接受缓存的话,或者说只有一个缓存的话,为了保证接受的数据是按顺序传输的,所以如果位于x序号之后的序号分组先到达目的主机的运输层的话必然丢弃,这样的话将在重传上花费很大的开销,所以一般如果有过大的序号达到接收端,那么会按照序号缓存起来等待之前的序号分许到达,然后一并交付到应用进程。

TCP可靠传输的详细过程:

发送发伪代码:

[html]view plain copy print?

1. NextSeqNum = InitialSeqNumber  

2. SendBase = InitiSeqNumber  

3.   

4. loop(forever){  

5.     switch(event){  

6.   

7.         event: 接收来自应用程序产生的数据data放入到发送缓存中  

8.                 if(定时器没有开启)  

9.                     start_timer  

10.                 将包装后的报文段交付给网络层形成IP数据报  

11.                 NextSeqNum = NextSeqNum + length(data)  

12.         break;  

13.   

14.         event:如果定时器超时  

15.               发送那个没有确认的序号最小的分组  

16.               start_timer  

17.         break;  

18.   

19.         event:接收到ACK,将ACK中的确认号赋给y  

20.               if(y > SendBase){  

21.                 SendBase = y;  

22.                 if(目前还有尚未确认的部分){  

23.                     start_timer  

24.                 }  

25.               }else {  

26.                 开始对y进行计数  

27.                 if(发送ACK的确认号为y的个数为3个的时候){  

28.                     重新发送序号为y的哪一个分组  

29.                 }  

30.               }  

31.         break;  

32.     }  

33. }  

接收方:

若分组在之前未被接收过,则被缓存并且发送ACK(这个ACK是上一个完整交付了的最后一个分组的序号);如果检查序号接受过则丢弃。

若该分组的序号等于接收窗口的继续号,则该分组以及以前缓存的序号连续的分组(交付给上层)。发送ACK。

其他情况,忽略该分组。

猜你喜欢

转载自blog.csdn.net/xiaonan153/article/details/81510293