TCP协议建立连接(三次握手)和断开连接(四次挥手)

首先先看下TCP的报头格式
TCP报头格式
发现在中间有一个6位的标志位(红色标出)

标志位字段 含义
URG 紧急指针是否有效;置为1表示要优先处理
ACK 确认号是否有效;设为1表示为确认应答报文(通常情况下会设为1,但是第一次的TCP不会设为1)
PSH 提示接收端应用程序立刻从TCP缓冲区把数据读走(若接收端缓冲区数据长时间未处理,那么发送端将强制将数据交付)
RST 复位报文段,对方要求重新建立连接;置为1表示要复位
SYN 同步报文段,请求建立连接;置为1表示连接请求报文,其他类型置为0
FIN 结束报文,通知对方,本端要关闭了(是根据五元组(源IP,目的IP,源端口号,目的端口号,协议号)进行关闭)

TCP建立连接和断开连接流程

三次握手,四次挥手

1 三次握手

三次握手

1.1 三次握手过程

首先服务端和客户端都处于CLOSED状态

1.1.1 第一次握手
  • 客户端:客户端发送请求建立连接,想要请求连接,需要发送SYN包到服务器,此时客户端的状态为SYN_SEND状态

    为什么是客户端请求连接,而不是服务端请求连接??
    想想现实中的例子,比如qq,都是用户想连接上qq服务器,但是qq服务器不会主动想要连接一个用户

  • 服务端:服务端在最开始的时候是ClOSE状态,然后是处于监听状态LISTEN,然后接收到客户端发送的SYN包,服务器端变为SYN_RECVE状态

    为什么服务端要处于监听状态??
    因为服务器端的某个SOCKET处于监听状态,表示可以接受连接了

注:在第一次握手的时候ACK是为0的状态,因为ACK是确认应答报文,第一次握手的过程是客户端向服务端发送连接请求,连连接都还没有建立,那么就没有确认应答这一说了

1.1.2 第二次握手
  • 服务端:服务端此时状态为SYN_RECVE状态,此时接收到了客户端已经发送过来的SYN包,此时服务端要做的是对客户端的SYN包进行确认(ACK),并且也发送一个STY包

    服务端在第一次握手时收到客户端发送的SYN(请求连接)之后为什么还要再向客户端发送SYN请求??
    建立连接是一个相互的过程,开始是客户端请求连接,服务端收到后又要再次向客户端发送请求,双方都需要请求连接

  • 客户端:此时客户端接收到服务端发送的确认应答(ACK),和一个SYN包,此时客户端的状态变为ESTABLISHED(已建立连接)状态

1.1.3 第三次握手
  • 客户端:客户端此时的状态为ESTABLISHED,此时收到了服务端发送的确认应答和服务端的连接请求,那么要想建立连接,客户端就向服务端发送一个确认应答(ACK)
  • 服务端:服务端此时接收到客户端的确认应答(ACK),表示请求收到了,那么此时的状态变为ESTABLISHED状态,表示连接建立,此时双方连接建立完成,可以开始通信了

1.2 三次握手举例(两个人打电话)

打电话举例

1.3 三次握手的一些问题

为什么是三次握手不是两次握手或者四次握手或者更多??

先看两次握手的情况:

  • 客户端向服务端发送SYN连接请求
  • 服务端接收,并向客户端发送确认应答报文ACK和再次向服务端发送连接请求

    两次握手的过程就是上面的两个过程,但是我们知道TCP的报文发送是有可能发生丢失问题的
    (1)现在假设第一步出现丢失情况:客户端向服务端发送的SYN包发生丢失,此时服务端不会接收到任何信息,当然就不会对客户端的请求做出任何反应,因此双方都认为连接未达成,不会产生问题
    (2)现在假设第二步出现丢失的状况:服务端给客户端发送ACK和SYN发生丢失,此时站在服务端一方看来,服务端认为自己将两条报文发送完成了,那么自己已经完成了连接,所以服务器端会维护一个链接;但是此时因为ACK和SYN(可能是其中一个或者两个都丢了)客户端没有(或收到不全)收到服务端的回复,那么站在客户端看来,双方并没有建立完成连接,此时可能又会再次发送连接请求,如果都恰巧一直发生这种情况,那么在服务端会维护无数个实际上并没有用处的连接,这样会浪费很大的资源(“SYN的洪水攻击”)

再看三次握手的情况:

  • 客户端发送连接请求SYN报文
  • 服务端发送确认应答ACK和连接请求SYN
  • 客户端对服务端发送的连接请求SYN发送ACK确认应答报文

    三次握手的过程就是上面写道的三步,但是在传输的过程中依旧可能会发生报文丢失的问题
    (1)假设第一步报文出现丢失问题:客户端发送请求连接报文SYN,但是在传输途中报文丢失,服务端没有接收到SYN报文,因此不会对客户端进行响应,因此双方都认为连接未建立,不会产生问题
    (2)假设第二步报文出现丢失问题:服务端接收到客户端发送的SYN,但是在给客户端进行响应回复的ACK和SYN在传输途中丢失了,那么客户端也就不会对服务端的发送进行响应,双方认为连接未建立,不会产生问题
    (3)假设第三部报文出现丢失问题:客户端对服务端发送的报文进行响应,站在客户端来说,ACK确认应答已发送,那么客户端认为连接建立,那么会在客户端上维护连接,但是ACK在传输途中丢失,那么服务端未收到确认应答,那么站在服务器端,服务器认为连接为建立成功,那么服务器上不会维护连接,那么不会产生问题(为什么在客户端有无用的连接维护不会影响,但是服务端无用连接维护会产生问题??客户端有很多,而服务端的空间有限)
    综上,三次握手可以

其实四次握手或者多次握手也是可以的,但是并没有必要,三次足以解决问题,并且每次建立握手都是需要耗费空间的,因此不需要那么多次

2 四次挥手

四次挥手

2.1 四次挥手过程

所谓四次挥手其实就是客户端与服务端断开连接,当然还是客户端主动要断开

2.1.1 第一次挥手
  • 客户端:客户端向服务端发送结束报文FIN,此时客户端的状态为FIN_WAIT
  • 服务端:服务端接收FIN,此时状态为CLOSE_WAIT
2.1.2 第二次挥手
  • 服务端:服务端向客户端发送断开连接的确认应答ACK
  • 客户端:客户端接收服务端发送的ACK,进入FIN_WAIT2状态

此时从客户端到服务器的连接断开

2.1.3 第三次挥手
  • 服务端:服务端向客户端发送结束报文FIN,此时服务端状态为LAST_ACK
  • 客户端:客户端接收服务端的结束报文,此时状态为TIME_WAIT(此时客户端并不是关闭的状态,而是TIME_WAIT的状态)
2.1.4 第四次挥手
  • 客户端:客户端向服务端发送确认应答请求
  • 服务端:此时服务端状态变为CLOSED

四次挥手结束,最终客户端与服务端的状态都变为CLOSED

2.2 四次挥手的一些问题

2.2.1 为什么TIME_WAIT状态要等待2MSL才能回到CLOSED状态

经过四次挥手过后,按道理说,客户端的状态可以直接回到CLOSED的状态,但是通过上面的过程图发现,在客户端在到CLOSED状态之前还到了TIME_WAIT状态,这是因为网络很可能是不可靠的,在报文传输的时候很有可能会发生报文丢失的情况,无法保证客户端最后发送的ACK报文一定会被对方收到,就是说对方处于LAST_ACK 状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT 状态的作用就是用来重发可能丢失的ACK报文。

2.2.2 关闭TCP一定要四次挥手吗

不一定,4次挥手关闭TCP连接是最安全的做法。但有时是不太希望出现TIME_WAIT 状态(比如当MSL数值设置过大导致服务器端有太多TIME_WAIT状态的TCP连接,减少这些条目数可以更快地关闭连接,为新连接释放更多资源),这时我们可以通过设置SOCKET变量的SO_LINGER标志来避免SOCKET在close()之后进入TIME_WAIT状态,这时将通过发送RST强制终止TCP连接(取代正常的TCP四次握手的终止方式)。但这种方法并不优先选择,使用TIME_WAIT的方法是优先考虑的。

猜你喜欢

转载自blog.csdn.net/L_X_Y_HH/article/details/81978528