linux系统上验证SO_REUSEADDR作用的一次实验
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((interface, port))
SO_REUSEADDR设为1表示可以重用处于TIME_WAIT状态的端口
SO_REUSEADDR选项要在sock.bind()调用之前设置
发起方表示发起中断连接的一方,这个可以是客户,也可以是服务器,响应方则是响应中断的一方,可以是客户,也可以是服务器。
在linux上做个试验.代码如下
#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter03/tcp_sixteen.py
# Simple TCP client and server that send and receive 16 octets
import argparse, socket
def recvall(sock, length):
data = b''
while len(data) < length:
more = sock.recv(length - len(data))
if not more:
raise EOFError('was expecting %d bytes but only received'
' %d bytes before the socket closed'
% (length, len(data)))
data += more
return data
def server(interface, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((interface, port))
sock.listen(1)
print('Listening at', sock.getsockname())
while True:
print('Waiting to accept a new connection')
sc, sockname = sock.accept()
print('We have accepted a connection from', sockname)
print(' Socket name:', sc.getsockname())
print(' Socket peer:', sc.getpeername())
message = recvall(sc, 16)
print(' Incoming sixteen-octet message:', repr(message))
sc.sendall(b'Farewell, client')
sc.close()
print(' Reply sent, socket closed')
def client(host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, port))
print('Client has been assigned socket name', sock.getsockname())
sock.sendall(b'Hi there, server')
reply = recvall(sock, 16)
print('The server said', repr(reply))
sock.close()
if __name__ == '__main__':
choices = {'client': client, 'server': server}
parser = argparse.ArgumentParser(description='Send and receive over TCP')
parser.add_argument('role', choices=choices, help='which role to play')
parser.add_argument('host', help='interface the server listens at;'
' host the client sends to')
parser.add_argument('-p', metavar='PORT', type=int, default=1060,
help='TCP port (default 1060)')
args = parser.parse_args()
function = choices[args.role]
function(args.host, args.p)
运行服务器监听localhost的1060端口,并且使用如下选项
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
服务器运行截图
然后在localhost上的其他端口运行一个客户端,跟服务器建立连接
查看服务器信息,连接成功并断开了连接
此时终止服务器进程,然后输入netstat指令查看网络状态,此时虽然套接字的连接已经断开,系统中仍能看到一个处于TIME_WAIT状态的连接,因为是服务器先调用close,所以是服务器(127.0.0.1:1060)进入TIME_WAIT状态
此时马上重新运行服务器进程,是成功的,即虽然127.0.0.1:1060还在TIME_WAIT状态,另一个进程也可以马上重用这个地址来监听套接字。
我们中断所有进程,设置SO_REUSEADDR为0,重复上述操作
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0)
其他的信息跟第一次试验是一样的,除了重新启动服务器时就会报错,这就是SO_REUSRADDR选项的作用了,设为0时就不允许重用处于TIME_WAIT状态下的连接