TCP系列02—连接管理—1、三次握手与四次挥手

时间:2023-03-09 22:46:28
TCP系列02—连接管理—1、三次握手与四次挥手

一、TCP连接管理概述

正如我们在之前所说TCP是一个面向连接的通信协议,因此在进行数据传输前一般需要先建立连接(TFO除外),因此我们首先来介绍TCP的连接管理。

通常一次完整的TCP数据传输一般包含三个阶段,分别是连接建立(setup)、数据传输(established)和连接释放(teardown 也称为cleared 或 terminated)。连接管理部分的主要内容则是TCP连接建立与连接释放的方式以及TCP连接状态(connection state)的管理,另外在建立TCP连接的过程中,在发送端和接收端会交换一些选项(option),其中一些选项只能在建立连接的过程中进行交换,有一些则可以在随后的数据传输中进行交换,因此我们也会分别简单介绍一下这些常见的选项。

在TCP的连接建立过程中一般需要处理下面三个问题

  1. 要使每一方能够确知对方的存在。
  2. 要允许双方协商一些参数(如最大报文段长度,最大窗口大小,服务质量等)。
  3. 能够对传输实体资源(如缓存大小等)进行分配

TCP建立连接最常见的方式就是通过三次握手(three-way handshake),连接释放最常见的方式则是四次挥手(four-way handshake或four-way wavehang),下面我们先介绍这两种最常见的连接管理机制。

二、三次握手

三次握手的整个过程如下图所示,一般我们称呼主动发起连接(Active Opener)的一端为客户端(Client)被动等待连接(Passive Opener)的称为服务器端(Server)。此处我们说的客户端和服务器端和我们在WEB开发中使用客户端和服务器端的概念略有差异,WEB开发中一般称呼用户侧为客户端,后台为服务器端,但是后台的服务器端也可以主动向客户端发起TCP连接(尽管这种场景少一些),这个时候后台服务器也就相当于我们下图中的客户端了。

TCP系列02—连接管理—1、三次握手与四次挥手d

下面我们介绍一下整个过程

  • A和B的初始状态都是关闭状态,B进入LISTEN状态后被动打开,此时B等待接收客户端的SYN包建立连接。
  • A主动打开时,A 的 TCP 向 B 发出连接请求报文段,其首部中的同步位 SYN = 1,选择序号 seq = x,表明传送报文时的第一个字节序号是 x,由于SYN标志在逻辑上占用一个系列号,因此实际数据传输的时候,TCP传输的数据中第一个Byte对应的系列号为x+1。这个SYN包发送以后,A则进入SYN_SENT状态,等待B回复ACK确认包。
  • B 的 TCP 收到连接请求报文段后,则发回确认。B 在确认报文段中应使 SYN = 1,使标志位 ACK = 1, 其确认号ack = x + 1,自己选择的序号 seq = y。记得我们之前说过序列号SN实际代表传输了多少比特的数据净荷,实际上在TCP的SYN包中一般并不携带数据,但是由于SYN包和FIN包在协议规定在逻辑上面占1个Byte,因此B在接收到这个SYN包后回复的ack=x + 1。后面我们会讲到消耗系列号SN就意味着这个数据包一旦丢失可以进行重传操作,由于SYN消耗一个byte的系列号,因此SYN数据包丢失的时候是会触发重传的。在B收到A的SYN包并且发送ACK确认包后,B则进入SYN_RCVD状态。
  • A 收到此报文段后向 B 给出确认,其 ACK = 1,确认号 ack = y + 1。此时A进入ESTABLISHED状态,A 的 TCP 通知上层应用进程,连接已经建立。
  • B 的 TCP 收到主机 A 的确认后,B也进入ESTABLISHED状态,同时通知其上层应用进程当前TCP 连接已经建立。

一般来说上面用来建立连接的初始系列号ISN(即x和y)的值是随机选取的,后面的文章会简单介绍ISN的生成。

三、四次挥手

数据传输结束后,通信的双方都可释放连接。现在如下图假设A 的应用进程先向其 TCP 发出连接释放报文段,并停止发送数据,主动关闭 TCP连接。则四次挥手的过程如下图所示

TCP系列02—连接管理—1、三次握手与四次挥手

四次挥手释放连接的流程如下

  • 初始状态下A和B都是处于ESTABLISHED状态,当应用层没有待发数据而指示A关闭TCP连接的时候,A 设置连接释放报文段首部的标志位 FIN = 1,ACK=1,其序号seq = u,确认号ack=v,等待 B 的确认。此时A进入FIN_WAIT_1状态
  • B 收到A的FIN包的时候,发出确认,由于FIN包与SYN包类似都在逻辑上占1byte,因此确认号 ack = u + 1,而这个报文段自己的序号 seq = v。此时B进入CLOSE_WAIT状态,TCP 服务器进程通知高层应用进程。
  • 当A收到B的ACK确认包后,A进入FIN_WAIT_2状态,关于这个状态我们后续在进一步介绍。
  • 若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。B 设置连接释放报文首部的FIN=1,ACK=1,报文序列号seq=v,确认号ack=u+1。此时B进入LAST_ACK状态。
  • A 收到连接释放报文段后,必须发出确认,在确认报文段中 ACK = 1,确认号 ack = v + 1,自己的序号 seq = u +1。 此时A进入TIME_WAIT状态。在TIME_WAIT状态下,A经过2MSL时间后就进入关闭状态,关于TIME_WAIT状态我们后续进一步介绍。
  • 在B接收到A的确认包后,B立即进入关闭状态。A和B都进入关闭状态后整个TCP连接释放。

四、三次挥手

在四次挥手关闭TCP连接的时候,有时会省略第二条ACK消息,只存在第一条FIN消息、第三条FIN+ACK消息以及第四条FIN消息,从上图四次挥手的过程中可以看到其实第二条消息的ACK Number和第三条消息的ACK Number是相同的,省略第二条的时候其实是第三条消息捎带了第二条消息的ACK,后面完整介绍TCP的状态机的时候,会看到这种省略第二条消息的状态消息。

五、wireshark抓包

对于上面讲到的三次握手和四次挥手,通过wireshark抓包来观察一下

TCP系列02—连接管理—1、三次握手与四次挥手

其中进行连接的两个socket(还记得我们前面章节说过一个ip地址+一个端口构成一个socket,或者叫做endpoint吧),一个是127.0.0.1:49324,对应我们前面示意图中的active opener和active closer,另外一个socket是127.0.0.1:9877,则对应passive opener和passive closer。注意wireshark中的系列号Seq呈现的是相对值,所以第一条消息会呈现Seq=0,实际值为0xbc069a3b。wireshark图中[SYN, ACK]表示TCP头中SYN标志位和ACK标志位有效,[SYN]、[FIN, ACK]等含义类似。将wireshark中SYN、FIN两个标志位,以及系列号Seq、应答号Ack与前面的示意图对应起来观察一下吧。

对于三次挥手的情况,wireshark抓包如下为了使两端的seq更容易区分,三次握手后我从client端向server端传输了10bytes数据,如下图高亮的条目所示,传输10bytes后进行三次挥手的TCP连接终止的过程

TCP系列02—连接管理—1、三次握手与四次挥手

补充说明

1.TCP连接的建立和终止可以参考RFC793协议

2.第二版<TCP/IP Illustrated Volume1>中给出的四次挥手过程中最后一条消息的seq是错误的