理解TIME_WAIT(2)

在前面一篇文章理解TIME_WAIT解释了TIME_WAIT出现的原因以及场景,TIME_WAIT出现在socket主动close()端。好了,现在问题来了

1. 为什么那些web server,比如Nginx和Apache上还是能看到很多TIME_WAIT?
2. Web服务器上提供服务一般都是一个端口,为什么这个端口上出现TIME_WAIT还是能正常服务?

下面动手做个小测试来学习

Server

先写一个基于TCP的socket server端,监听本地的9243端口,一旦有客户端的tcp连接就accept,然后调用recv,接着close,运行server.py

import sys
import socket

HOST = ''
PORT = 9243
DATA_BUFFER = 4096

#create a TCP socket
try :
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print 'Socket created'
except socket.error, msg :
    print 'Failed to create socket. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
    sys.exit()

# Bind socket to local host and port
try:
    s.bind((HOST, PORT))
except socket.error , msg:
    print 'Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1]
    sys.exit()

s.listen(5)         # allow 5 simultaneous

while True:
    # wait for next client to connect
    connection, address = s.accept()        # connection is a new socket
    data = connection.recv(DATA_BUFFER) # receive up to 1K bytes
    connection.close()

netstat查看服务端监听在9243端口

tcp        0      42.120.7.71:9243                0.0.0.0:*                   LISTEN      26487/python

测试

客户端用nc模拟向server端发数据包,指定源端口是5555

场景1: client主动断开连接

client向server发包,不发送数据,直接Ctrl+C,会导致client主动关闭socket
$ nc -p 5555 42.120.7.71 9243

由于client主动close,导致client进入TIME_WAIT状态
$ netstat -natp | grep 9243
tcp        0      42.120.7.71:9243                0.0.0.0:*                   LISTEN      26487/python
tcp        0      0 42.120.7.71:5555            42.120.7.71:9243            TIME_WAIT   -

客户端进入TIME_WAIT状态,这时候端口资源已经被占用,在一段时间内(Linux上大概1一分钟)不能再使用此端口
$ nc -p 5555 42.120.7.71 9243
nc: bind failed: Address already in use

场景2:server主动断开连接

client 向 server发包,发送数据,然后回车
$ nc -p 5555 42.120.7.71 9243
hello world

server 收到数据后主动调用了close,所以server进入TIME_WAIT
$ netstat -natp | grep 9243
tcp        0      42.120.7.71:9243                0.0.0.0:*                   LISTEN      26487/python
tcp        0      0 42.120.7.71:9243            42.120.7.71:5555            TIME_WAIT   -

场景3: server端主动断开连接后,client重新连接

client 向 server发包,发送数据,然后回车
$ nc -p 5555 42.120.7.71 9243
hello world

server 收到数据后主动调用了close,所以server进入TIME_WAIT
$ netstat -natp | grep 9243
tcp        0      42.120.7.71:9243                0.0.0.0:*                   LISTEN      26487/python
tcp        0      0 42.120.7.71:9243            42.120.7.71:5555            TIME_WAIT   -

client再次向server发包,发送数据,然后回车,发现竟然连接上了!!!
$ nc -p 5555 42.120.7.71 9243
hello world

client换个源端口向server发包,发送数据,然后回车
$c -p 5556 42.120.7.71 9243
hello world

server依然处于TIME_WAIT,两个TIME_WAIT状态
$ netstat -natp | grep 9243
tcp        0      42.120.7.71:9243                0.0.0.0:*                   LISTEN      26487/python
tcp        0      0 42.120.7.71:9243            42.120.7.71:5556            TIME_WAIT   -
tcp        0      0 42.120.7.71:9243            42.120.7.71:5555            TIME_WAIT

场景3的例子,明明是server端主动端口tcp连接,导致server进入TIME_WAIT,按照之前的解释处于TIME_WAIT状态的端口是不可使用的,但是client去连接却任然可以使用,而且还是正常通信了?再来做一个测试。

场景4: server端主动断开连接后,重启server端

启动server端
$ python server.py

client 向 server发包,发送数据,然后回车
$ nc -p 5555 42.120.7.71 9243
hello world

重启server端,发现bind端口失败
$ python server.py
Bind failed. Error Code : 98 Message Address already in use

server端主动close,尽管server程序退出,但是tcp连接依然存在,处于TIME_WAIT状态,端口资源不可用
$ netstat -natp | grep 9243
tcp        0      0 42.120.7.71:9243            42.120.7.71:5555            TIME_WAIT   -

总结

怎么解释场景3的现象呢?在TCP/IP详解里面是这样解释的,上面的现象违反了TCP规范,但是被大多数伯克利版本实现所支持,这些实现运行一个新的连接请求到达仍处于TIME_WAIT状态的连接,只要新的序号大于老连接的最后序号即可,这个特性可以让客户程序和服务程序能够连续的重用每一端的相同端口,但这个只有在服务器执行主动关闭才有效。这个就涉及到TCP中SN的生成逻辑了,但是在场景3中,我tcpdump出来,新的连接确实比老的连接SN要大,等以后研究了SN的生成逻辑再回过头来思考这个问题。

标签:none