问题发生场景
在做app客户端tcp掉线自动重连的时候,我把一个 socket 对象在应用层断开了,然后我考虑到内存碎片问题(内存的内部碎片,外部碎片),就没有释放这个socket,而是直接用它再去连接其他服务器,发现连不上,我向这个socket写任何数据都没有反应!然而我申请一个新的socket去连接,又一切正常。
为什么向断开的socket再写数据就没有任何回应了呢?
原因
经过一番查找终于找到了原因,socket断开之后,会进入一个叫TIME_WAIT的状态,该状态会持续4到5分钟,在这个状态内,socket不会收发任何来自应用层的数据,看下图(图片来源于网络)
看上图,客户端在发出最后一个ack包之后,进入了 TIME_WAIT 状态!
因为最后一个 ack 可能会丢失,如果丢失后会触发服务器tcp超时重传机制,导致服务器再发个 FIN 包给你,所以要进入 TIME_WAIT 等待4分钟,确保网络上没有残余的 FIN 包,才能进入 CLOSED 状态,进行下一次连接。在 TIME_WAIT 状态中不能建立新的连接,假设你建立新的连接,可能会收到服务器超时来的FIN包,会断掉的。
为什么是等待4分钟,因为有个最大生存时间MSL,最后一个ack包过去需要一个MSL,服务器重传一个FIN包需要一个MSL,加起来就是 2MSL,一个MSL大约2分钟,两个就是4分钟。所以4分钟后,tcp就能够确定网络上没有FIN包了,这时socket才可以发起新的连接。
简单说说4次挥手,3次挥手
既然上了张4次挥手的图,那得顺带说说吧。
为什么是4次挥手?
因为tcp是全双工的,有收,发两个通道,关闭一个通道需要2次,关闭两个就需要4次了。
客户端发出FIN包,客户端进入FIN-WAIT-1状态
服务器收到FIN包,服务器进入CLOSE-WAIT状态
剩下的状态看上面那个图吧,最后主动关闭socket的一方会进入TIME_WAIT,最终双方都CLOSE了,完成4次挥手。
既然说了4次挥手,3次握手也得顺带说说吧
为什么是3次握手?
首先完全可靠的通信并不存在。
- 3次握手可以基本排除两方通信设备的故障。(一方的收或发有故障了,3次握手也完成不了,自己想想是不是)
- 3次握手可以保护接收方的资源。(接收方要给发起方准备socket,准备线程空间,要消耗系统资源的,不敢随便建立,必须要发起方两次确定在线后才能建立连接)
- 防止残余的包建立新的请求