2.1.3 TCP连接的断开
在完成数据传输后,需要通过一个更加复杂的“四次握手”流程来断开TCP连接。下面我们趁热打铁,介绍一下TCP连接断开的过程。
TCP连接断开的整个过程如图2-6所示。我们假设当客户端希望断开与服务器之间的连接时,客户端将要发送的TCP数据包序列号为k,用于确认服务器之前传输的数据包的确认号为l,那么此时TCP连接断开的过程就可以描述为下面的四次握手。
第一次握手
客户端向服务器发送一个数据包,目的是告诉服务器,自己希望与它断开连接。为了说明这个意图,会在这个数据包的头部字段中说明以下几点。
图2-6 TCP连接断开
● 我收到了你刚才发来的消息,期待你的下一条消息(将ACK标记设置为1,确认号的值设置为l)。
● 我现在希望和你断开TCP连接(将头部的FIN标记设置为1)。
● 我把这个消息序列号提供给你(将序列号字段的值设置为k。注意,TCP再次作出规定,FIN数据包可以携带数据,且无论是否携带数据,均占用一个序列号。我们在这里假设,TCP连接断开阶段的四次握手数据包均不携带数据)。
第二次握手
服务器收到客户端发来的断开连接请求之后,断开了客户端到服务器的连接,并向客户端回复一个TCP数据包,目的是告诉客户端下述信息。
● 我收到了你发来的连接断开请求,期待你的下一条消息(将ACK标记设置为1,确认号的值设置为k+1)。
● 告诉你这个消息的序列号(将序列号设置为l)。
第三次握手
这一次还是服务器发送给客户端的消息,这个消息的作用是为了请求客户端断开客户端到服务器的连接,在这个消息中,服务器表示:
● 我收到了你之前发来的连接断开请求,期待你的下一条消息(将ACK标记设置为1、确认号的值设置为k+1,因为第一次握手之后,客户端不会再向服务器发送数据了);
● 我现在希望和你断开TCP连接(将头部的FIN标记设置为1);
● 我把这个消息序列号提供给你(将序列号字段的值设置为ll。因为虽然第一次握手之后,客户端就不会再向服务器发送其他数据了,但第二次握手之后,服务器只断开了客户端到服务器的连接,服务器到客户端的连接依然存在,因此在第三次握手之前,服务器可能还会给客户端发送一些其他占用序列号的消息)。
第四次握手
客户端收到服务器发来的断开连接请求之后,也断开了服务器到客户端的连接,并向客户端回复一个TCP数据包,这个最后的数据包对服务器说:
● 我收到了你发来的连接断开请求(将ACK标记设置为1,确认号的值设置为ll+1。注意,即使在这条消息中,客户端仍会向服务器提供确认号);
● 告诉你这个消息的序列号(将序列号设置为k+1)。
上述过程就是TCP连接建立与断开的完整步骤。读者在这里不妨思考一下,为啥断开会话的流程非得比建立会话的过程多握一次手呢?
简言之,建立连接就像打电话,电话接通之前双方都还没开始交流,主叫方自然是有话要说,被叫方看到来电显示之后只要愿意接起来,就表示被叫方这会儿起码也想听听对方要说啥,因此双方在同一时间都有意愿建立通信。断开连接则像挂电话,一方说“没事了”想挂电话肯定不算完,遇到对方是个话密的人,再拉住你强聊一个小时都不算长。这个类比可以对应TCP建立和断开连接的两个流程中:在建立TCP连接的时候,因为响应方会在第二次握手时对SYN加以响应,同时自己发起SYN;而在断开TCP连接时,响应方在接收到FIN时,自己很有可能还“有话要说”,所以响应方第二次握手可以仅仅对发起方的FIN进行确认(即发送ACK),在自己也准备断开会话时再通过第三次握手发起FIN。
TCP建立和断开连接的步骤对于此后的学习至关重要。不过,这个过程倒是不用在这里就开始死记硬背,我们推荐读者在理解这个流程的基础上把图2-5和图2-6所示的连接建立和断开示意图看个“眼熟”,以此来熟悉TCP连接建立与断开的过程。
在本节最后,我们通过表2-1向读者介绍几个常用的基于TCP的应用层协议。在我们平常使用这些协议时,数据都会在经过传输层处理时封装上那样的TCP头部,也都会与对端的设备按照上面的过程建立和断开TCP连接。
表2-1 几个常见的基于TCP的应用层协议
TCP的学习总算可以告一段落了。虽然在这一章的后半部分,我们还需要足足介绍三个网络层协议。别急,因为那三个协议的篇幅加在一起,大概也只能占到TCP篇幅的一半。下面,我们先来看看和TCP同样工作在传输层的UDP。