在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接
服务端状态转化
[CLOSED -> LISTEN] 服务器端调用listen后进入LISTEN状态,等待客户端连接;
[LISTEN -> SYN_RCVD] 一旦监听到连接请求(同步报文段),就将该连接放入内核等待队列中,并向客户端发送SYN确认报文。
[SYN_RCVD -> ESTABLISHED] 服务端一旦收到客户端的确认报文,就进入ESTABLISHED状态,可以进行读写数据了。
[ESTABLISHED -> CLOSE_WAIT] 当客户端主动关闭连接(调用close),服务器会收到结束报文段,服务器返回确认报文段并进入CLOSE_WAIT;
[CLOSE_WAIT -> LAST_ACK] 进入CLOSE_WAIT后说明服务器准备关闭连接(需要处理完之前的数据);当服务器真正调用close关闭连接时,会向客户端发送FIN,此时服务器进入LAST_ACK状态,等待最后一个ACK到来(这个ACK是客户端确认收到了FIN)
[LAST_ACK -> CLOSED] 服务器收到了对FIN的ACK,彻底关闭连接
客户端状态转化
[CLOSED -> SYN_SENT] 客户端调用connect,发送同步报文段;
[SYN_SENT -> ESTABLISHED] connect调用成功,则进入ESTABLISHED状态,开始读写数据;
[ESTABLISHED -> FIN_WAIT_1] 客户端主动调用close时,向服务器发送结束报文段,同时
进入FIN_WAIT_1;
[FIN_WAIT_1 -> FIN_WAIT_2] 客户端收到服务器对结束报文段的确认,则进入FIN_WAIT_2,开始等待服务器的结束报文段;
[FIN_WAIT_2 -> TIME_WAIT] 客户端收到服务器发来的结束报文段,进入TIME_WAIT,并发出LAST_ACK;
[TIME_WAIT -> CLOSED] 客户端要等待一个2MSL(Max Segment Life,报文最大生存时间)的时间,才会进入CLOSED状态。
下图是TCP状态转换的一个汇总:
较粗的虚线表示服务端的状态变化情况;
较粗的实线表示客户端的状态变化情况;
CLOSED是一个假想的起始点,不是真实状态
上述只是TCP三次握手四次挥手的一个总览,接下来我将具体为大家介绍以下连接是如何建立的,又是如何断开的,以及为什么要叫三次握手四次挥手
三次握手其实就是客户端服务器三次的通信的过程
举一个例子,如果一个男生和一个女生要成为男女朋友,那么彼此就会是对方唯一的那个人,那么就会出现以下对话
这样一来,他们就算是建立连接了,但是我们可以发现这里其实是四次通信的过程。这是因为在这个例子中我们的女生是不是可以将两句话合成一句话给男生发过去,这样一来就变成了三次握手
而我们的TCP也正是如此做的,由于封装一个TCP报文很麻烦,所以为了提高效率,就将两条信息包装在一起就行发送了
这里我们用了一个标志位SYN,该标志的作用是:请求建立连接;我们把携带SYN标识的称为同步报文段
上面所举得例子中我们也可以看出,这三次交互才使得他们二人男女朋友关系建立完成。
这样可能看不出效果,我们对比一下两次“握手”的场景进行对比一下,比如上述例子的一下场景
这时候就出现问题了:女生是男生的唯一,但是男生是女生的唯一还是备胎,男生也就不知道,这样男女朋友就算是没有建立了(舔狗除外)
这里也为大家举个例子吧,还是一对情侣,他们异地,然后他们现在要打游戏连麦,交互过程如下:
男:听的我说话吗?
女:听得到,你听的到我说话吗?
男:听的到,那我们开始吧!
第一次交互,这个过程中,当女生听到男生说得话,这时候
女生:
男生:
第二次交互,女生给男生回应,这时候
女生:
男生:
第三次交互,男生给女声回应,这时候女生收到后
女生:
男生:
这时候他们就知道对方和自己的发送能力和接收能力都正常,就可以开始通信
TCP的三次握手也是如此。
LISTEN:服务器的状态
表示服务器已经准备就绪,随时可以有客户端来建立连接了.
相当于手机开机信号良好,随时可以有人来打电话了.
ESTABLISHED:客户端和服务器都有.
连接建立完成接下来就可以正常通信了.
相当于电话拨打过去,对方接通了.
了解了三次握手之后,四次挥手也就好理解,也就是四次交互的过程。
举个例子吧,比如现在一对情侣要分手了,他们之间就会有以下交互
这里的情侣不再是彼此的唯一,也就断开了连接
在TCP中,我们请求断开的请求使用标志位FIN标记,作用为:通知对方,本端要关闭了,四次握手过程如下:
这里有可能就会有人有疑问呢?为什么这里的女生的话不可以合并了呢?
这里呢,女生可能因为生气或者心已经不再男生这里,所以回复消息的速度变慢,或者有什么事儿耽搁了,所以消息就没有及时回复
在TCP断开连接的过程中,通常情况下请求与回应合并是不可以的,特殊情况下可以
和不合并取决于两者的发送的时机是否相同,三次握手中间两次之所以可以合并,是因为三次握手的时机相同,该三次握手都是纯内核中完成的(应用程序感知不到,也无法干预),内核在收到syn报文时会立即发送ack也会立即发送syn。
下面所见内容可能会涉及到TCP服务器于客户端的建立,如果对着方面不了解的小伙伴,可以去看看博主写的【JavaEE初阶】 TCP服务器与客户端的搭建,了解后再进行观看
在我们四次挥手过程中
这就是为什么不能合并的原因了
注意:对于 CLOSE WAIT,一般而言,对于服务器上出现大量的 CLOSE_WAIT 状态,原因就是服务器没有正确的关闭 socket,导致四次挥手没有正确完成。这是一个 BUG。只需要加上对应的 close 即可解决问题。
这里的TIME_WAIT表示当前连接不要立即释放,而是需要等待一会,那为什么需要等待呢?
我们需要注意的是,在四次挥手过程中同样存在丢包,超时重传现象,
如果是最后一个ACK丢包了,站在服务器的视角来看,服务器是不知道是因为ACK丢了,还是自己发的FIN丢了,所有统-视为FIN丢了,统一进行重传操作.
既然服务器可能要重传FIN,客户端就需要能够针对这个重传的FIN进行ACK响应.很明显,如果刚才彻底把连接释放了,这样的ACK就无法进行了.
因此使用TIME_ WAIT状态保留一定的时间, 就是为了能够处理最后-一个ACK丢包的情况,能够在收到重传的FIN之后,进行ACK响应.
这个时间是多长呢?是2MSL
想一想,为什么是TIME_WAIT的时间是2MSL?
MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话
就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据,但是这种数据很可能是错误的);
同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失,那么服务器会再重发一个FIN。这时虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK);
关于《【JavaEE初阶】 TCP三次握手四次挥手(超详细版)》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!