介绍了 TCP 协议的定义、特点及报文的首部格式,今天我们来详细介绍 TCP 协议是如何实现面向连接并确保可靠传输的。
通过三次握手建立连接
TCP 提供面向有连接的通信传输,面向有连接是指在数据通信开始之前先做好通信两端之间的准备工作。
TCP 的连接建立,我们常常称为三次握手。三次握手的目的是为了确保对连接双方来说,链路都是通的。
三次握手除了双方建立连接外,主要还是为了沟通一件事情,就是 TCP 包序列号的问题。每个连接都要有不同的序列号,这个序列号的起始序号是随着时间变化的。
建立了连接之后,为了维护这个连接,双方都要维护一个状态机,在连接建立的过程中,双方的状态变化时序图如下:
注:在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个已收到消息的通知,这个消息叫做确认应答(ACK)。
一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。客户端收到服务端发送的 SYN 和 ACK 之后,发送 ACK 的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。服务端收到 ACK 的 ACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。
至此,就建立起了客户端与服务端的连接,双方可以进行数据传输了。
如何实现可靠的传输
TCP 通过肯定的确认应答(ACK)实现可靠的数据传输,当发送端将数据发出之后会等待对端的确认应答。如果确认应答,说明数据已经成功达到对端。反之,则数据有可能丢失。在一定时间内没有等到确认应答,发送端就认为数据已经丢失,并进行重发。因此,即使产生了丢包,仍然能够保证数据能够到达对端,实现可靠传输。
未收到确认应答不一定是数据一定丢失,有可能是对方已经收到数据,只是返回的确认应答在途中丢失,这种情况也会导致发送端因为没有收到确认应答而重新发送数据。
此外,也有可能因为一些其他原因导致确认应答延迟到达,在源主机重发数据以后才到达的情况也屡见不鲜,此时,对源主机没啥影响,但是对目标主机来说,它会反复收到同样的数据。而为了对上层应用提供可靠的传输,必须放弃重复的数据包。为此,就必须引入一种机制,能够识别是否已经接收数据,又能够判断是否需要接收。
上述这些确认应答处理、重发控制以及重复控制等功能都可以通过序列号实现,序列号是按顺序给发送数据的每一个字节都标上号码的编号。
接收端查询接收数据 TCP 首部中的序列号和数据长度,将自己下一步应该接收的序列号作为确认应答发送回去,就这样,通过序列号和确认应答号,可以有效解决顺序问题和丢包问题,TCP 就可以实现可靠传输:
如果在指定的时间内,发送到目标主机的 TCP 包没有得到相应的确认应答,则会进行重发,这个等待的时间就是重发超时时间。这个重发超时的具体计算逻辑如下:
TCP 在每次发包时都会计算往返时间(RTT)及偏差(抖动),将这个往返时间和偏差相加,重发超时的时间,就是比这个总和稍微大一点的值。
在 BSD Unix 和 Windows 中,超时都是按照 0.5 秒为单位进行控制,因此,重发超时都是 0.5 秒的整数倍。不过由于最初的数据包还不知道往返时间,因此其重发超时一般设置为 6 秒左右。
数据被重发之后若还是收不到确认应答,则再次发送。此时等待确认应答的时间将会以 2 倍、4 倍的指数函数增长。
此外,数据也不会被无限、反复重发,达到一定重发次数后,如果仍没有确认应答返回,就会判断为网络或对端主机发生了异常,强制关闭连接。并且通知应用通信异常强行终止。
TCP 除了提供面向连接的可靠传输之外,还能对流量进行控制,从而在空闲时加大传输数据量提高通信效率,在繁忙时减少传输数据量避免网络拥塞。关于 TCP 的流量控制实现将在下一篇给大家具体介绍。