TCP/IP协议(三次握手、四次挥手、TCP状态转换)

TCP/IP协议(三次握手、四次挥手、TCP状态转换)

我们先了解一下TCP的面向连接体现在:

  1. 两端通信之前,必须先建立连接。
  2. 连接一旦建立,则两端的交互过程都是建立在这一连接上完成的,无需重新建立连接,直至关闭连接为止。
  3. 通信完成后,必须断开连接,以释放服务器的资源。
  4. 操作系统为我们维护连接,所以内核需要为连接分配相应的资源。
  5. 对于服务器而言,一个连接只能为一个客户端服务,从而造成服务器性能有待优化。

 

那么这时候我们就可以先看一下TCP建立连接的过程:三次握手

 

再看一下,连接建立成功后,数据传输的过程:

 

最后看一下TCP断开连接的过程:四次挥手

 

很多人看到这里,可能会问,为什么建立连接是3次握手,而断开连接是4次挥手呢?

主要是:

  • 对于建立连接的3次握手:主要是初始化SYN(同步序列号),通信的双方要互相通知自己初始化的SYN,也就是图中的x和y,这个号码要作为以后的数据通信的序号,以保证接收到的数据不会因为网络环境的影响而乱序。(TCP会用这个序号来拼接数据)
  • 对于断开连接的4次挥手:其实仔细看应该是2次,只不过TCP是全双工的。所以发送方和接收方都需要FIN和ACK。只不过有一方是主动的,另一方是被动的,所以看上去就成了所谓的4次挥手,如果两边同时发起断开连接申请,那么两边就会同时进入到CLOSING状态,然后到达TIME_WAIT状态,2MSL时间后,最终进入到CLOSED状态。

下面是双方同时断开连接的示意图:

双方同时断开连接的流程:

  • 双方同时获取到应用程序传来的close信号,同时发送FIN信号,并进入FIN_WAIT1状态。
  • 双方同时获取到对端传过来的FIN信号,则同时发送ACK确认,并进入CLOSING状态。
  • 双方同时获取到对端传过来的ACK信号,则同时进入TIME_WAIT状态。
  • 等待2个MSL时间,时间到了之后,则进入CLOSED状态,双方完成断开连接操作。
  • MSL:是Maximum Segment Lifetime英文的缩写,中文为“报文最大生存时间”,指的是任何报文在网络上可以存在的最长时间,超过这个时间报文会被遗弃。因为MSL是由源主机设置初始值,并不是存的时间,每经过一个路由器的时候,这个值 -1,当值减到0的时候发送ICMP报文通知源主机,且这个数据报不再发送到下一个路由器上,相当于数据报被丢弃了。规定中MSL是2min,但是实际应用中常用的是30S,1min和2min等。

 

另一个问题:3次握手都是必须的吗,可以少一次或者多一次吗。

答:因为TCP是全双工的,所以双方都得明确自己发送的消息对方可以成功接收,对方发送的消息自己也可以成功接收。

下面我们可以分开看看三次握手这3次的必要性:

第一次握手:服务器端成功获取了客户端发送过来的信息。

 

第二次握手:客户端成功过获取了服务器端发送回来的确认信息。

 

第三次握手:服务器端成功获取了客户端发送过来的确认信息。

 

这三张图很完美的诠释了为什么需要三次握手。

也说明了三次握手少一次或者多一次的问题:

  • 少一次握手:只能少第三次握手,也就是客户端发送了请求,服务器端收到之后做出回应并给其预留资源,但若是少了第三次握手,则服务器端不能保证客户端可以准确接收到自己发出的响应信息,若是客户端真的没有接收到,则可能再次发出请求,那么服务器端接收到之后还得再次预留资源,那么这样的客户端多了以后,会大量浪费服务器资源,且服务器可能会崩溃。
  • 多一次握手:没有必要,会浪费网络资源。

 

最后,我们再逐步分析一下4次挥手:

我们最终发现,TCP断开连接的四次挥手过程,也是缺一不可的。

 

另外,还有几个事情需要注意一下:

①:建立连接时SYN超时了:比如server端接到了clien发的SYN,并且返回了SYN_ACK,但是client掉线了,server端没有获取到client返回回来的ACK,那么,这个连接就处于一个中间状态,即没有成功,也没有失败。

处理规则:server端在一定时间内没有收到的client返回的ACK,则TCP会重发SYN_ACK。在Linux下,默认重发次数为5次,重发的间隔时间从1s开始,每次翻一番,则5次的重发时间间隔分别为1s, 2s, 4s, 8s, 16s,总共31s,第5次发出后再等上32s(16*2)后还没有接收到client的ACK,则知道第5次也超时了,那么TCP就会将这个连接断开。全程加起来一共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 2^6 -1 = 63s。

 

②:TIME_WAIT存在的必要性: 我们注意到在四次挥手过程中,从TIME_WAIT状态到CLOSED状态,会有一个超时装置,这个超时装置设置为2*MSL,那么为什么会有TIME_WAIT状态,为什么不直接转换成CLOSED状态?

主要有两个原因:(TIME_WAIT状态只会出现在主动断开方

  • 保证迟来的数据可以被识别并丢弃掉。因为网络环境的不稳定性,所以有的数据可能还在路上,所以得将这些数据识别之后丢弃掉,保证不让后边的连接者接收到奇奇怪怪的数据。
  • 保证可以可靠的终止TCP连接。有可能因为各种原因,导致对端(被动关闭的一方)没有成功接收到ACK,那么被动关闭的一方在一定时间后,就会重新发送FIN,这时主动关闭的一方就知道了ACK没有成功发送过去,那么就回重新发送,这样子一来一回,时间刚好在2*MSL之内,这也是为什么TIME_WAIT状态需要等2*MSL才转换成CLOSED状态。

那么如果TIME_WAIT状态出现,这个端口号被占用了,但是我还是想启用这个端口号怎么办呢?

我们这里有两个方法:

  • 一个字:等,等上个2MSL,这个端口号会被释放,就可以接着使用了。
  • 通过socket选项设置,通过设置SO_REUSEADDR(重用本地地址),就可以直接继续使用了。

图 5-5 为socket全部选项:

 

 

最后看一下TCP的状态转换:

①TCP的状态一共分为:

  • LISTEN:提供某种服务,侦听远方TCP端口的连接请求,当提供的服务没有被连接时,处于LISTENING状态,端口是开放的,等待被连接。
  • SYN_SENT:客户端调用connect,发送一个SYN请求建立一个连接,在发送连接请求后等待匹配的连接请求,此时状态为SYN_SENT
  • SYN_RECEIVED:在收到和发送一个连接请求后,等待对方对连接请求的确认,当服务器收到客户端发送的申请连接信号时,将标志位ACK和SYN置1发送给客户端,此时服务器端处于SYN_RCVD状态,如果连接成功了就变为ESTABLISHED,正常情况下SYN_RCVD状态非常短暂。
  • ESTABLISHED:双方已完成连接,三次握手完毕。
  • FIN_WAIT_1:等待远程TCP连接中断请求,或先前的连接中断请求的确认,主动关闭端应用程序调用close,TCP发出FIN请求主动关闭连接,之后进入FIN_WAIT_1状态。
  • FIN_WAIT_2:从远程TCP等待连接中断请求,主动关闭端接到ACK后,就进入了FIN_WAIT_2 这是在关闭连接时,客户端和服务器两次握手之后的状态,是著名的半关闭的状态了,在这个状态下,应用程序还有接受数据的能力,但是已经无法发送数据,但是也有一种可能是,客户端一直处于FIN_WAIT_2状态,而服务器则一直处于WAIT_CLOSE状态,而直到应用层来决定关闭这个状态。
  • CLOSE_WAIT:等待从本地用户发来的连接中断请求,被动关闭端TCP接到FIN后,就发出ACK以回应FIN请求,并进入CLOSE_WAIT. 
  • CLOSING:等待远程TCP对连接中断的确认,处于此种状态比较少见,常出现在双方同时关闭连接时
  • LAST_ACK:等待原来的发向远程TCP的连接中断请求的确认,被动关闭端一段时间后,接收到文件结束符标志数据传输完毕的应用程序将调用CLOSE关闭连接,TCP也发送一个 FIN,等待对方的ACK进入LAST_ACK。
  • TIME_WAIT:在主动关闭端接收到FIN后,TCP就发送ACK包,并进入TIME-WAIT状态等待足够的时间以确保远程TCP接收到连接中断请求的确认,很大程度上保证了双方都可以正常结束,但是也存在问题,正常情况下也需要等待2MSL时间后,才能进行下一次连接。
  • CLOSED:被动关闭端在接受到ACK包后,就进入了closed的状态,连接结束,没有任何连接状态。

 

②状态迁移过程:

服务器端:CLOSED -> LISTEN -> SYN_RCVD -> ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED

客户端:CLOSED -> SYN_SENT -> ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED

完整的状态转移图:它描绘了所有的TCP状态以及可能的状态转换

 

至此,TCP协议初步认识完成。

猜你喜欢

转载自blog.csdn.net/IT_Quanwudi/article/details/86558753