TCP协议为什么可靠?

0.405字数 1544阅读 5585

我们都知道,TCP和UDP协议的区别在于TCP可以提供可靠的网络数据传输,但UDP不能。

为什么TCP比较可靠呢?可能有人会回答,TCP是面向连接的,而UDP不是。连接是什么呢?它是一个像水管一样的东西,把所有的数据往连接里一放就保证了数据完整有序到达吗?其实不是的。

TCP/IP详解对整个数据传输过程有着详细的说明。我试着把其中关键的部分抽取出来,解释一下TCP协议到底是怎么保证传输的可靠性的。

建立连接

连接是什么

当提到连接,我们本能的会把它想成一根水管或者绳子,建立连接就是把这个水管或者绳子接起来。在通信的时候,我们会把连接想象成一个独立的通道,建立连接后所有的数据都顺着这个通道传输出去,对方就能收到有序完整的数据。但是实际上,因为各种各样的问题(硬件故障、网络阻塞、攻击等)的存在,网络传输通道本身就不可靠。
所以,TCP里的所谓连接不是一个通道,它只是通信双方建立的一个一对一的逻辑关系,让双方都明确对方是自己的通信目标。

既然双方只存在逻辑约定,数据仍然可能在传输过程中会出现错误、丢失等各种状况。那建立连接的意义是什么呢?举个例子:

如果一开始双方没有建立连接的那几步沟通,乙不知道甲到底要干什么,也不知道甲说的内容从什么地方开始到什么地方结束,甲乙就都没法确保这次通信的完整和正确。

更多的细节可以参考到底什么是TCP连接?

三次握手建立连接

TCP通过三次握手的方式建立连接,具体的过程见下图:

从图中可以看到,三次握手的过程其实是一个客户端和服务器各向对方发送一个数seq,并接收对方的ACK(收到的seq+1)的过程。第二次握手的数据里同时包含了服务器给客户端的ACK和服务器发出的seq。
三次握手以后,连接双方就同时进入ESTABLISHED(连接成功)状态,准备开始数据传输。

如果想知道三次握手建立连接,或者四次挥手断开连接的更多细节,可以看看简析TCP的三次握手与四次分手这篇文章。
另外,知乎上为什么TCP是三次握手,而不是两次或者四次握手这个问题,解释了三次握手的设计初衷,值得一看。

传输数据

ACK机制

由于通信过程的不可靠性,传输的数据不可避免的会出现丢失、延迟、错误、重复等各种状况,TCP协议为解决这些问题设计了一系列机制。
这个机制的核心,就是发送方向接收方发送数据后,接收方要向发送方发送ACK(回执)。如果发送方没接收到正确的ACK,就会重新发送数据直到接收到ACK为止。
比如:发送方发送的数据序号是seq,那么接收方会发送seq + 1作为ACK,这样发送方就知道接下来要发送序号为seq + 1的数据给接收方了。

我们来看看在不同的异常情况下,ACK机制是怎么工作的:

  • 数据丢失或延迟。发送方发送数据seq时会起一个定时器,如果在指定时间内没有接收到ACK seq + 1,就把数据seq再发一次。
  • 数据乱序。接收方上一个收到的正确数据是seq + 4,它返回seq + 5作为ACK。这时候它收到了seq + 7,因为顺序错了,所以接收方会再次返回seq + 5给发送方。
  • 数据错误。每一个TCP数据都会带着数据的校验和。接收方收到数据seq + 3以后会先对校验和进行验证。如果结果不对,则发送ACK seq + 3,让发送方重新发送数据。
  • 数据重复。接收方直接丢弃重复的数据即可。

ACK的优化

按照ACK机制,只要整个数据传输顺利结束,接收方就能收到完整有序的数据了。但是,如果我们针对每一个数据包都发送ACK,就会有大量的网络资源消耗在ACK的发送上,这不太划算的。于是,TCP设计了延迟ACK的机制。

这个机制其实很简单。客户端一次给服务器发送多个数据包,当服务器收到客户端的数据包时,不马上发送ACK,而是稍微等一小段时间。在这个过程中服务器可能能收到后续几个数据包,服务器就可以直接按照最后一个正确的数据发送ACK,减少发送ACK的总数。

除了延迟ACK的机制,TCP还做了很多对传输过程的优化,比如滑动窗口机制,比如慢启动机制。由于跟本文的主题无关,我就不在这里多说了,有兴趣的同学可以搜索来看看。


本人学识有限,文中难免有不严谨或者错误出现,希望各位读者能帮忙指出,感谢。

推荐阅读更多精彩内容