学习笔记:三次握手和四次挥手

126 阅读7分钟

前言:TCP连接总共分为三个过程

1.建立TCP连接:三次握手(橙色)

2.数据传输:可以进行HTTP/HTTPS报文传输(绿色)

3.释放TCP连接:四次挥手(红色)

image.png

简单描述三次握手和四次挥手:

三次握手

第一次握手:客户端敲门说,在吗?
第二次握手:服务端听到后愿意理它回答道,在,啥事?
第三次握手:客户端听到后喜滋滋打开门凑近说,开下门,我跟你说,今天在路上……
服务端听到有八卦可听,立马把门开了
至此,两端TCP连接已成功建立

四次挥手:

第一次挥手:客户端说,我跟你没话说了,准备关门。
第二次挥手:服务端听到后说,好的我知道你准备关门了
第三次挥手:服务端说,我也要关门了
第四次挥手:客户端听到后说,好的,你关门吧
服务端收到后,啪的一下把门关了
客户端等待2MSL时间后发现服务端真没话说了,也把门关了
至此,两端TCP连接已成功关闭

相关知识:

客户端:主动发起TCP连接建立称为TCP客户(client)

服务端:被动等待TCP连接建立的应用进程称为TCP服务器(server)

SYN:Synchronization简写,意为同步。初始值为0,置为1表示开启。

ACK:Acknowledge简写,意为确认。初始值为0,置为1表示开启。

seq:sequence简写,意为序号,客户端和服务端都有各自的序号,是随机生成并由各自维护(每次发送报文时+1)

ack:acknowledge简写,意为确认号,客户端和服务端都将收到对方的序号seq + 1后设为自己的确认号ack,并在发送报文时携带

FIN:Finish简写,意为结束。初始值为0,置为1表示开启

MSL:Maximum Segment LifeTime,意为报文最大生成时间。TCP客户进程在发送完最后一个TCP确认报文段后,在经过2MSL时长,就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的TCP连接中,不会出现旧连接中的报文段。

正文:

1.最初两端的TCP都处于关闭阶段

2.服务端首先创建传输控制块,用来存储TCP连接中的一些重要信息。此时服务端进入了监听(LISTEN)阶段

3.客户端首先也是创建传输控制块,然后开始建立TCP连接,接下来就是正式的三次握手:

三次握手

image.png

1.第一次握手:

客户端:
    发送TCP连接请求报文,报文段首部,SYN置为1,seq随机生成,初始序号为x。
    此时进入SYN-SENT(同步请求已发送)阶段

2.第二次握手:

服务端:
    收到客户端的TCP连接请求报文后,如果同意建立连接,那么
    发送TCP连接请求确认报文,报文段首部,SYN置为1,ACK置为1,seq随机生成,初始序号为y,ack=x+1(确认号为客户端序号x + 1)。
    此时进入SYN-RECEIVED(同步请求已接收)阶段

3.第三次握手:

注意:TCP规定,普通的TCP确认报文段可以携带数据。但如果不携带数据则不消耗序号,在这种情况下所发送的下一个数据报文段的序号仍是x + 1

客户端:
    收到服务端的TCP连接请求确认报文后,
    发送普通的TCP确认报文,报文段首部,ACK置为1,seq=x+1,ack=y+1(确认号为服务端序号y + 1)。
    此时进入ESTANLISHED(已连接)阶段

服务端:
    收到TCP确认报文后,此时客户端也进入ESTANLISHED(已连接)阶段

4.现在TCP双方都进入了连接已建立状态,他们可以基于已建立好的TCP连接进行可靠的数据传输了

5.任何一方都可以在数据传送结束后发出连接释放的通知,此处以客户端主动断开连接为例:

四次挥手

image.png

1.第一次挥手:

客户端:
发送TCP连接释放报文@1,报文段首部,FIN置为1,ACK置为1,seq=u,ack=v。(经过多次数据传输,序号相比于初始值已经增加了很多,此处用新的变量表示)
此时进入FIN-WAIT-1(请求终止等待1)阶段,客户端不再会发送TCP报文给服务端

2.第二次挥手:

服务端:
    收到客户端发送的TCP连接释放报文后,
    服务端发送普通的TCP确认报文,报文段首部,ACK置为1,seq=v,ack=u+1。
    此时服务端进入CLOSED-WAIT(关闭等待)阶段
    此时从客户端到服务端这个方向的连接就释放了,但是从服务端到客户端方向的连接并未关闭,若服务端仍有数据要发送,客户端仍要接收
客户端:
    收到服务端发送的普通TCP确认报文后
    此时客户端进入FIN-WAIT-2(请求终止等待2)阶段

3.第三次挥手:

服务端
    发送TCP连接释放确认报文,报文段首部,FIN置为1,ACK置为1,seq=w,ack=u+1。
    此时进入LAST-ACK(最终确认)阶段

4.第四次挥手:

客户端:
    发送普通的TCP确认报文,报文段首部,ACK置为1,seq=u+1,ack=w+1。
    此时进入TIME-WAIT(延时等待)阶段
    等待2MSL时间后,进入CLOSED(关闭)阶段

服务端:
    收到客户端发送的TCP确认报文后,进入CLOSED(关闭)阶段

至此,四次挥手结束,两端处于关闭阶段

相关问题

1.客户端在发送完最后一个确认报文后,为什么不直接进入关闭状态?而是要进入时间等待状态,2MSL后才进入关闭状态?
image.png

由于这个ACK报文段可能会丢失,使得处于LAST-ACK状态的服务端收不到对已发送FIN报文段的确认,从而会触发超时重传。服务器会重发FIN报文段,客户端能保证在2MSL时间内收到来自服务器的重传FIN报文段,从而客户端重新发送ACK应答报文段,并重置2MSL计数。
假如客户端不等待2MSL就直接进入CLOSE状态,那么服务端会一直处于LAST_ACK状态。
在经过2MSL时长,就可以使本次连接持续时间内所产生的所有报文段都从网络中消失,这样就可以使下一个新的TCP连接中,不会出现旧连接中的报文段。

得出结论,等待2MSL时间的意义:

1.保证客户端最后发送的ACK能够到达服务器,帮助其正常关闭
2.防止已失效的连接请求报文段出现在下次连接中

2.为什么需要四次挥手,而不是像三次握手那样3次就终止连接?

这是由于TCP的半关闭(half-close)造成的。半关闭是指:TCP提供了连接的一方在结束它的发送后还能接受来自另一端数据的能力。通俗来说,就是不能发送数据,但是还可以接受数据。

TCP不允许连接处于半打开状态时,就单向传输数据,因此完成三次握手后才可以传输数据(第三握手可以携带数据)。

当连接处于半关闭状态时,TCP是允许单向传输数据的,也就是说服务器此时仍然可以向客户端发送数据,等服务器不再发送数据时,才会发送FIN报文段,同意现在关闭连接。

这一特性是由于TCP双向通道互相独立所导致的,也使得关闭连接必须经过四次握手。

可能有些人会有疑惑:为什么中间的ACK和FIN不可以像三次握手那样合为一个报文段呢?

在socket网络编程中,执行close()方法会触发内核发送FIN报文。什么时候调用close()方法,这是由用户态决定的,假如服务器仍有大量数据等待处理,那么服务器会等数据处理完后,才调用close()方法,这个时间可能会很久,而ACK报文则是由系统内核来完成的,这个过程会很快。所以中间的ACK和FIN不能合为一个包。