图解TCP连接建立与释放

1.前言
2.TCP状态机
3.TCP连接的建立
4.同时打开连接请求
5.TCP连接释放
6.同时关闭连接
7.TCP相关疑问

1.前言

  TCP是面向连接的,可靠的字节流协议。因此,在传输数据之前通信双方必须建立一个TCP连接,建立TCP连接需要在服务器和客户端之间进行三次握手。通信双方数据传输完毕之后连接释放,释放连接需要在通信双方之间进行四次挥手。

2.TCP状态机

  TCP所谓的“连接”,只是通信双方维护一个“连接状态”,让它看上去好像有连接一样,其实TCP连接是虚拟的连接,不是电路连接。首先看下TCP的状态机,状态机是TCP连接和释放的全过程。如下图所示:
  图解TCP连接建立与释放_第1张图片

  下面针对TCP状态机所出现的各个状态进行简要的分析:
  

  • CLOSED: 表示初始状态。对服务端和客户端双方都一样。
  • LISTEN: 表示监听状态。服务端调用了listen函数使其处于监听状态,此时可以accept(接收)客户端连接。
  • SYN_SENT: 表示客户端已经发送了SYN报文段,则会处于该状态。当客户端调用connect函数发起连接请求时,首先发SYN给服务端,然后自己进入SYN_SENT状态,并等待服务端发送SYN+ACK作为请求应答。
  • SYN_RCVD: 表示服务端收到客户端发送SYN报文段。服务端收到这个报文段后,进入SYN_RCVD状态,然后发送ACK+SYN给客户端。
  • ESTABLISHED: 表示TCP连接已经成功建立,通信双方可以开始传输数据。服务端发送完ACK+SYN并收到来自客户端的ACK后进入该状态,客户端收到来自服务器的SYN+ACK并发送ACK后也进入该状态。
  • FIN_WAIT_1: 表示主动关闭连接。 无论哪方调用close函数发送FIN报文都会进入这个状态。
  • FIN_WAIT_2 : 表示被动关闭方同意关闭连接。主动关闭连接方收到被动关闭方返回的ACK后,会进入该状态。
  • TIME_WAIT : 表示收到对方的FIN报文并发送了ACK报文,就等2MSL后即可回到CLOSED状态。如果FIN_WAIT_1状态下,收到对方同时带FIN标志和ACK标志的报文时,可以直接进入TIME_WAIT状态,而无须经过TIME_WAIT_2状态。
  • CLOSING : 表示双方同时关闭连接。如果双方几乎同时调用close函数,那么会出现双方同时发送FIN报文的情况,就会出现CLOSING状态,表示双方都在关闭连接。
  • CLOSE_WAIT : 表示被动关闭方等待关闭。当收到对方调用close函数发送的FIN报文时,回应对方ACK报文,此时进入CLOSE_WAIT状态。
  • LAST_ACK : 表示被动关闭方发送FIN报文后,等待对方的ACK报文状态,当收到ACK后进入CLOSED状态。

3.连接的建立

  TCP连接的正常建立过程通信双方需要三次握手,其过程如下图所示:
  
  图解TCP连接建立与释放_第2张图片

  TCP协议提供可靠的可靠的连接服务,采用有保障的三次握手方式来创建一个TCP连接。三次握手的过程如下:
  

  1. 客户端进程向服务器进程发出连接请求,请求报文段首部中的控制位标志SYN=1,由于是首次请求建立连接,因此,控制位标志ACK=0,该报文段包含计算机随机生成的初始序号seq=x。发送请求连接的TCP报文段,此时客户端进程处于SYN_SENT状态,这是TCP连接的第一次握手。
  2. 服务端收到客户端发来的请求报文后,若同意建立连接,则向客户端发送确认。确认报文中的控制位SYN=1,ACK=1,确认应答号ack=x+1(即在接收到的序列号值基础上加1),并且发送主机的一个初始序列号seq=y(即请求与客户端连接)。此时,服务器端进入SYN_RCVD状态,这是TCP连接的第二次握手。
  3. 客户端进程收到服务端进程的确认报文后,还要向服务端发出确认信息。确认报文段的控制位ACK=1,确认应答号ack=y+1(即在接收到序列号值基础上加1),此时,客户端进入ESTABLISHED状态。这是TCP连接的第三次握手。此时,TCP连接成功建立。

4.同时打开连接请求

  正常情况下,通信一方请求建立连接,另一方响应该请求,但是如果出现,通信双方同时请求连接时,则连接建立过程并不是三次握手过程,而且这种情况的连接也只有一条,并不会建立两条连接。同时打开连接时,两边几乎同时发送SYN,并进入SYN_SENT状态,当每一端收到SYN时,状态变为SYN_RCVD,同时双方都再发SYN和ACK作为对收到的SYN进行确认应答。当双方都收到SYN及相应的ACK时,状态变为ESTABLISHED。其过程如下:

图解TCP连接建立与释放_第3张图片

5.TCP连接释放

  由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。原则是主动关闭的一方发送一个FIN报文来表示终止这个方向的连接,收到一个FIN意味着这个方向不再有数据流动,但是另一个方向仍能发送数据,直到另一个方向也发送FIN报文。TCP连接释放的过程如下图所示:

图解TCP连接建立与释放_第4张图片

以下是释放连接的四次挥手过程:

  1. 客户端进程主动向服务端发出连接释放请求报文,并停止发送数据,主动关闭TCP连接。释放连接报文段中控制为FIN=1,序列号为seq=i,发送该报文段后客户端进入FIN_WAIT_1(终止等待1)状态,等待服务器确认。这是TCP连接释放的第一次挥手。
  2. 服务器收到连接释放请求报文段后即发出确认释放连接的报文段,该报文段中控制位ACK=1,确认号为ack=i+1,然后服务器进入CLOSE_WAIT(关闭等待)状态。此时TCP处于半关闭状态,即客户端已经不向服务器发送数据,但服务器仍可向客户端发送数据。这是TCP连接释放的第二次挥手。
  3. 客户端收到服务器的确认信息后,就进入了FIN_WAIT_2(终止等待2)状态,等待服务器发出连接释放请求报文段,若没有数据需要传输,服务器被动向客户端发出链接释放请求报文段,报文段中控制位FIN=1,序列号seq=j,此时服务器进入LAST_ACK(最后确认)状态,等待客户端的确认应答,这是TCP连接释放的第三次挥手。
  4. 客户端收到服务器的连接释放请求后,必须对此发出确认。确认报文段中控制位ACK=1,确认应答号ack=j+1,客户端发出确认应答消息后进入TIME_WAIT(时间等待)状态。在这段时间内TCP连接并没有释放,必须等待2MSL时间后,客户端才进入CLOSED状态。服务器收到客户端的确认应答后,就进入CLOSED状态。直到客户端和服务器都进入了CLOSED状态后,连接就完全释放了,这是TCP连接释放的第四此挥手。

6.同时关闭连接

  正常情况下,通信一方请求连接关闭,另一方响应连接关闭请求,并且被动关闭连接。但是若出现同时关闭连接请求时,通信双方均从ESTABLISHED状态转换为FIN_WAIT_1状态。任意一方收到对方发来的FIN报文段后,其状态均由FIN_WAIT_1转变成CLOSING状态,并发送最后的ACK数据段。当收到最后的ACK数据段后,状态转变成TIME_WAIT,在等待2MSL时间后进入到CLOSED状态,最终释放整个TCP连接。其过程如下:
  
图解TCP连接建立与释放_第5张图片

7.TCP相关疑问

为什么在TCP协议里,建立连接是三次握手,而关闭连接却是四次挥手?
  因为当处于LISTEN状态的服务器端收到来自客户端的SYN报文(客户端希望新建一个TCP连接)时,它可以把ACK(确认应答)和SYN(同步序号)放在同一个报文里来发送给客户端。但在关闭TCP连接时,当收到对方的FIN报文时,对方仅仅表示对方已经没有数据发送给你了,但是你自己可能还有数据需要发送给对方,则等你发送完剩余的数据给对方之后,再发送FIN报文给对方来表示你数据已经发送完毕,并请求关闭连接,所以通常情况下,这里的ACK报文和FIN报文都是分开发送的。

为什么一定要进行三次握手?
  当客户端向服务器端发送一个连接请求时,由于某种原因长时间驻留在网络节点中,无法达到服务器端,由于TCP的超时重传机制,当客户端在特定的时间内没有收到服务器端的确认应答信息,则会重新向服务器端发送连接请求,且该连接请求得到服务器端的响应并正常建立连接,进而传输数据,当数据传输完毕,并释放了此次TCP连接。若此时第一次发送的连接请求报文段延迟了一段时间后,到达了服务器端,本来这是一个早已失效的报文段,但是服务器端收到该连接请求后误以为客户端又发出了一次新的连接请求,于是服务器端向客户端发出确认应答报文段,并同意建立连接。如果没有采用三次握手建立连接,由于服务器端发送了确认应答信息,则表示新的连接已成功建立,但是客户端此时并没有向服务器端发出任何连接请求,因此客户端忽略服务器端的确认应答报文,更不会向服务器端传输数据。而服务器端却认为新的连接已经建立了,并在一直等待客户端发送数据,这样服务器端一直处于等待接收数据,直到超出计数器的设定值,则认为服务器端出现异常,并且关闭这个连接。在这个等待的过程中,浪费服务器的资源。如果采用三次握手,客户端就不会向服务器发出确认应答消息,服务器端由于没有收到客户端的确认应答信息,从而判定客户端并没有请求建立连接,从而不建立该连接。

为什么TIME_WAIT状态必须等待2MSL时间,而不直接给进入CLOSED状态?
  主要有两个原因:
  
  

  1. TIME_WAIT确保有足够的时间让对端收到了ACK,如果被动关闭的那方没有收到ACK,就会出发被动端重发FIN。因为最后一次确认应答ACK报文段很有可能丢失,因而使被动关闭方处在LAST_ACK,此时被动关闭方会重复这个FIN+ACK报文段,在这等待的2MSL时间被主动关闭方重新收到这个被动关闭方重发的FIN+ACK报文段,因此,主动关闭方会重新发送确认应答信息,从而重新启动2MSL计时器,直到通信双方都进入CLOSED状态。如果主动关闭方在TIME_WAIT状态不等待一段时间就直接释放连接并进入CLOSED状态,那么主动关闭方无法收到来自被动关闭方重发的FIN+ACK报文段,也就不会再发送一次确认ACK报文段,因此被动关闭方就无法正常地进入CLOSED状态。

  2. 有足够的时间让这个连接不会跟后面的连接混在一起。防止已失效的请求连接出现在本连接中。在连接处于2MSL等待时,任何迟到的报文段将被丢弃,因为处于2MSL等待的,由该插口(插口是IP端口对的意思,socket)定义的连接在这段时间内将不能被再用,这样就可以使下一个新的连接中不会出现这种旧的连接以前延迟的报文段。

你可能感兴趣的:(tcpip详解)