假设一开始已经有一个连接在1234端口建立,我们关闭这个连接;过一会我们在同样的ip和端口建立连接,但是TCP必须防止在前一次连接中的老的报文在它原先的连接已终止后,还出现在这个新的连接中,因此,TCP将不允许在处于TIME_WAIT状态的ip和端口处建立新的连接。而2MSL时间过后,老的报文早已在网络中消失了,也就避免了这种情况的发生。
这种情况可以很容易通过《网络编程-一个简单的echo程序》的server程序来观察:
- $ ./server #在一个终端启动server,
- $ ./client 127.0.0.1 1234 #在另一个终端启动client
在服务端终端ctrl+c终止服务端,然后再次启动server:
- $ ./server
- bind error: Address already in use
- $ netstat -anop|grep :1234
- tcp 1 0 127.0.0.1:33722 127.0.0.1:1234 CLOSE_WAIT 11691/client off (0.00/0/0)
- tcp 0 0 127.0.0.1:1234 127.0.0.1:33722 FIN_WAIT2 - timewait (57.92/0/0)
终止服务端后,服务端处于TIME_WAIT状态,此时再次启动server,将不能使用原来的ip和端口建立连接,因此出现Address already in use的报错。
但是需要注意:
- 由于客户端通常使用的是临时端口(仔细观察会发现,客户端每次启动使用的端口基本都不一样),因此客户端即便处于TIME_WAIT状态,也不影响它马上再次启动
- 一些实现允许一个新的连接请求仍然处于TIME_WAIT状态的连接,只要新的seq大于该连接的前一个连接的最后序号
- 通过设置选项SO_REUSEADDR,可以让一个进程重新使用仍处于TIME_WAIT状态的socket
半打开的TCP连接
假设一个连接建立之后,突然有一方异常终止连接了,但是另一个不知道,这个时候TCP的连接就是半打开的。如果服务端不加处理,那么最终就会导致服务端有大量的半打开连接。那么服务端如何知道客户端的连接已经异常终止了呢?如果等待服务端发送数据出错时发现,那么这个时候可能已经太晚了。
幸运的是,TCP有保活定时器。即服务端可以通过设置保活选项来了解客户端是否已经终止连接。
通过下面的方式可以看到很多连接有这样的定时器:
- $ netstat -npo|grep keepalive
- tcp 0 0 192.168.0.103:50832 59.111.179.136:443 ESTABLISHED 5882/chrome keepalive (37.33/0/0)
- tcp 0 0 192.168.0.103:50638 154.8.131.191:443 ESTABLISHED 5882/chrome keepalive (0.00/0/0)
- tcp 0 0 192.168.0.103:59330 203.107.41.32:9026 ESTABLISHED 5882/chrome keepalive (0.35/0/0)
- tcp 0 0 127.0.0.1:45632 127.0.0.1:1080 ESTABLISHED 5886/firefox keepalive (335.28/0/0)
- tcp 0 0 192.168.0.103:49940 59.56.78.189:443 ESTABLISHED 5882/chrome keepalive (26.36/0/0)
但可惜的是,这样的定时器时间太长了,并且它不能代表应用程序能够正常工作,能够正常收发数据,因此应用层常常也会实现一个心跳机制。
总结
本文花了大量篇幅介绍了TIME_WAIT状态,这也是面试中常问的问题,重新梳理TCP的四次挥手是很有必要的。 (编辑:南京站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|