互联网公司,对网络通讯原理的掌握估计要求比较高,分分钟面试官第一个问题问你就是请描述一下TCP握手协议,为什么有3次握手和4次握手的区别。
如果要深入而且全面的理解这个协议,建议浏览相关专业书籍,例如《UNIX 网络编程》。本博客定位是让有这个基础概念的小白同学加深一下理解。
题外话,之所以写起这篇博客,源于周末约了一个勤奋加班的同学聚会,去了他的公司,恰好他遇到服务器上发现有大量端口处于CLOSE_WAIT状态的问题。于是回去后我就搜了相关资料,借此机会做个总结,也分享一下别人很好的案例。
先来上个原理简图:
上图描述分两个部分,
第一部分是客户端主动请求连接的过程。
第二部分是客户端主动请求关闭连接的过程。
- 三次握手例子通俗描述:
打电话的例子:
A : 你好我是A,你听得到我在说话吗
B : 听到了,我是B,你听到我在说话吗
A : 嗯,听到了
建立连接,开始聊天!
- 四次握手例子通俗描述:
A:“喂,我不说了。”A->FIN_WAIT1
B:“我知道了。等下,上一句还没说完。Balabala…..”B->CLOSE_WAIT | A->FIN_WAIT2
B:”好了,说完了,我也不说了。”B->LAST_ACK
A:”我知道了。”A->TIME_WAIT | B->CLOSED
A等待2MSL,保证B收到了消息,否则重说一次”我知道了”,A->CLOSED
问题一,3次握手协议和4次握手协议的区别是什么?
可以看出,TCP之所以被称为可靠传输协议,就是靠握手协议保证数据传输,通俗一点说,就是当你发送信号给对方,如何确保对方真的收到你的信号。而3次握手和4次握手的区别在于,3次握手是发生在请求连接的过程,4次握手是发生在关闭连接的过程。之所以关闭连接过程多了1个步骤,是因为被动关闭连接的一方在被关闭连接时候还处于发送数据未完成状态。如上图的下半部分ack M+1 不能和Fin N同时发送。ack M+1就是告诉对方“哦,我知道你要关闭了”,之后的 Fin才是告诉对方“我的数据也已经发送完毕了”。
问题二,发送方如何知道接收方究竟有没有收到呢?
继续深度思考,整个握手协议是为了让发送方确保接收方收到信息,那么上图下半部分最后一个步骤ack N+1发送出去后,发送方(客户端)如何知道接收方(服务端)究竟有没有收到呢?握手协议的设计魅力就体现出来了,这里涉及一个2MSL的概念。发送方(客户端)的逻辑是这样思考的,给接收方一个时间,在这个时间段内,假如接收方收不到信息,则接收方肯定会不断发送Fin N信号过来。换言之,如果接收方收到发送方(客户端)的确认信息,则在这个时间段内,发送方(客户端)不会收到接收方重复发过来的Fin N信号。而这个所谓的时间段,就被定义为两倍的MSL(Maximum Segment Lifetime)。
至此,有了上述基本的理解后,可以进入实战阶段了。
服务器发现大量CLOSE_WAIT状态的端口
案例原因总结:数据库连接问题导致服务器端线程阻塞,服务器线程阻塞导致服务器socket不能正常关闭。
案例原因总结:服务器端的socket编程程序关闭socket方式不正确,导致服务器socket没有彻底关闭。
解决方案:都是修改程序保证程序处理正常,关闭socket正常。
谨记,CLOSE_WAIT状态的一端都是被动关闭连接的,如果该端的socket程序没有正确关闭从而发送不了FIN信号,将会导致长时间CLOSE_WAIT状态。
服务器发现大量TIME_WAIT状态的端口
参看案例:http://blog.csdn.net/yusiguyuan/article/details/21445883
案例原因总结:通常这种情况发生在大量高并发短连接的应用场景,本身程序没什么错。出现这种大量TIME_WAIT状态源于主动关闭连接的一方(例如服务端)在2MSL等待时间里,有大量的高并发连接需要打开新的socket端口,而因为2MSL时间(例如2分钟)相比这些短连接处理完业务的时间(例如2秒钟)长,所以显得TIME_WAIT的socket时间占用过长。
解决方案:分多台服务器分担高并发连接请求。
想自己模拟一下CLOSE_WAIT?参考:http://qify.iteye.com/blog/1673564
另外,再继续深入拓展,上文对于握手协议的各个状态过程,并没有提及超时处理情形,可以想象,假如有大量CLOSE_WAIT端口,难道服务器没有保障措施自动处理这些问题?答案是有的,服务器有可能有个默认时间(这个时间可能默认很久),就是CLOSE_WAIT的端口多长时间没有反应就自动CLOSE的处理。同样,TIME_WAIT状态超时处理就是2MSL。当然,不同的系统对于这个异常处理的机制可能都不一样,要具体情况再看。