TCP通信中的三次握手和四次挥手


TCP连接建立与释放
TCP三次握手和四次挥手也就是我们TCP连接建立与释放的过程的一个形象描述。

连接前的一些工作
最初两端的TCP进程都处于关闭状态。一开始,TCP服务器进程首先创建传输控制块,用来存储TCP连接中的些重要信息。例如TCP连接表,指向发送和接收缓存的指针,指向重传队列的指针,当前的发送和接收序号等等。之后,就准备接受TCP客户辨程的连接请求。此时TCP服务器进程就进入监听状态,等待TCP客户进程的连接请求。

同理,TCP客户进程也是首先创建传输控制块。然后,在打算建立TCP连接时向TCP服务器选程发送TCP连接请求报文段。

TCP服务器进程是被动等待来自可P客户进程的连接请求,而不是主动发起,因此称为被动打开连接。

TCP连接建立(三次握手)


从图中,可以看出三次握手的基本步骤是:

第一次握手:TCP客户端向TCP服务器端发送连接请求报文SYN(seq=x),进入同步已发送状态(SYN_SEND),等待TCP服务器回应。TCP连接请求报文段首部中的同步位SYN被设置为1,表明这是一个TCP连接请求报文段。序号字段seq被设置了一个初始值x,作为TCP客户进程选择的初始序号。

注意:TCP规定SYN被设置为1的报文段不能携带数据,但要消耗掉一个序号。由于TCP连接建立是由TCP客户主动发起的,因此被称为主动打开连接。

第二次握手:TCP服务器端收到TCP客户端连接请求报文SYN(seq=x)后,将客户端的请求包SYN(seq=x)放入到自己的未连接队列,如果同意建立连接,此时TCP服务器需要发送两个内容给客户端;
  (1)向TCP客户端发送确认自己收到其连接请求的确认包ACK(ack=x+1),向客户端表明已知道了其连接请求。(这里ack值为x+1是对TCP客户进程所选择的初始序号的确认)
  (2)向TCP客户端发送连接询问请求包SYN(seq=y),询问客户端是否已经准备好建立连接,进行数据通信。(这里序号值随机,作为TCP服务器进程选择的初始序号)
即在第二次握手时TCP服务器向TCP客户端发送ACK(ack=x+1)+SYN(seq=y)包,该报文段首部中的同步位SYN和确认位ACK都设置为1,表明这是一个TCP连接请求确认报段。发送完该报文段此时服务器进入同步已接收状态(SYN_RCVD)。

请注意:这个报文段不能携带数据,因为它是SYN被设置为1的报文段,但同样要消耗掉一个序号。
 
第三次握手:TCP客户端收到TCP服务器的ACK(ack=x+1)+SYN(seq=y)报文后,知道了服务器同意建立连接,此时还需要发送连接已建立的消息给服务器。向服务器发送普通的TCP确认报文段,该报文段首部中的确认位ACK设置为1,序号字段seq被设置为x+1,这是因为TCP客户进程发送的第一个TCP报文段的序号为x,并且不携带数据,因此第二个报文段的序号为x+1。确认号字段ack被设置为y+1,这是对TCP服务器进程选择的初始序号的确认。发送该报文目的就是告诉服务器,我们之间已经建立了连接,可以进行数据通信。请注意:该报文是一个普通的TCP确认报文段,TCP规定普通的TCP确认报文段可以携带数据,但如果不携带数据,则不消耗序号。我们这里一般不携带数据,所以在这种情况下,发送的下个数据报的序号仍是x+1。

ACK(seq=x+1,ack=y+1)报文发送完毕,服务器收到后,此时服务器与客户端进入ESTABLISHED状态,开始进行数据传送。
  
下面是一个TCP三次连接示例:

 第一次握手:

第二次握手:

第三次握手:

TCP连接释放(四次挥手)


第一次挥手:TCP客户端进程发出连接释放报文,并且停止发送数据,进入终止等待1状态(FIN-WAIT-1)。该数据报首部中的终止位FIN和确认位ACK的值都被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认。序列seq字段的值设置为u(等于TCP客户进程之前已传送数据的最后一个字节的序号加1),确认号ack字段的值设置为v(等于TCP客户进程之前已收到的数据的最后一个字带的序号加1)。

注意:TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。

第二次挥手:TCP服务器进程收到TCP连接释放报文段后,会发送一个普通的TCP确认报文段并进入关闭等待状态(CLOSE_WAIT)。该报文段首部中的确认位ACK设置为1,表明这是一个普通的TCP确认报文段。序列seq字段的值设置为v(等于TCP服务器进程之前已传送过的数据的最后一个字节的序号加1,这也与之前收到的TCP连接释放报文段中的确认号匹配)。确认号ack字段的值设置为u+1,这是对TCP连接释放报文股的确认。TCP服务器进程这时通知高层的应用进程,TCP客户选程断开了与自己的TCP连接,从TCP客户进程至到TCP服务器进程这个方向的连接就释放了(但从TCP服务器进程到TCP客户进程这个方向的连接并未关闭)。这时候TCP连接属于半关闭状态,即TCP客户端进程已经没有数据要发送了,但是TCP服务器进程若发送数据,TCP客户端进程仍要接收。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

第三次挥手:TCP客户进程收到TCP确认报文段后就进入终止等待2状态(FIN-WAIT-2),等待TCP服务器进程发出的TCP连接释放报文(在这释放之前可能还需要接受服务器发送的最后的数据)。另外一边,若使用TCP服务器进程的应用进程已经没有数据要发送了,应用进程就通知其TCP服务器进程释放连接。TCP服务器进程发送TCP连接释放报文段并进入最后确认状态,该报文段首部中的终止位FIN和确认位ACK的值都被设置为1,表明这是一个TCP连接释放报文段,同时也对之前收到的报文段进行确认。现在假定序号seq字段的值为w,这是因为在半关闭状态下,TCP服务器进程可能又发送了一些数据。确认号ack字段的值为u+1,这是对之前收到客户端发过来的TCP连接释放报文段的重复确认。此时,服务器就进入了最后确认状态(LAST-ACK),等待客户端的确认。

注意:由于TCP连接释放是由TCP客户进程主动发起的,因此TCP服务器进程对TCP连接的释放称为被动关闭连接。

第四次挥手:TCP客户端进程收到TCP服务器进程的连接释放报文后,必须针对该报文段发送普通的TCP确认报文段。之后客户端就进入了时间等待状态(TIME-WAIT)。该报文段首部中的确认位ACK设置为1,表明这是一个普通的TCP确认报文段。序列seq字段的值设置为u+1,这是因为TCP客户进程之前发送的TCP连接释放报文段虽然不携带数据,但要消耗掉一个序号。确认号ack字段的值设置为w+1,这是对收到TCP连接释放报文段的确认。当另外一边TCP服务器进程收到该报文段后就进入关闭状态,但TCP客户进程还要2MSL(MSL是最长报文段寿命)的时间后才能进入关闭状态(CLOSED)。

服务器只要收到了客户端发出的确认,立即进入CLOSED状态。而客户端要等待一段时间才能进入CLOSED状态,结束了这次的TCP连接。因此服务器结束TCP连接的时间要比客户端早一些。

下面是一个TCP四次释放示例:

第一次挥手:

第二次挥手:

第三次挥手:

第四次挥手: