我有以下代码:
class Server:
def __init__(self, port, isWithThread):
self.isWithThread = isWithThread
self.port = port
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setblocking(0)
log.info("Socket created...")
def __enter__(self):
self.sock.bind(('127.0.0.1', self.port))
self.sock.listen(5)
log.info("Listening on %s:%s", '127.0.0.1', self.port)
return self
def __exit__(self, type, value, traceback):
self.sock.setblocking(1)
self.sock.close()
log.info("Socket closed.")
log.info("Bye")
def run(self):
#client, addr = self.sock.accept()
log.info('Selecting...')
readers, writers, errors = select.select([self.sock], [], [], 10)
log.debug(readers)
if readers:
client, addr = readers[0].accept()
log.info('Client: %s', client.recv(2048).decode())
client.sendall("Hippee!".encode())
client.close()
log.info("Disconnected from %s:%s", *addr)
有趣的是,当我拥有select.select
和setblocking(0)
时,最终会保留使用的地址。如果我删除setblocking
代码并将run
功能更改为:
def run(self):
client, addr = self.sock.accept()
log.info('Client: %s', client.recv(2048).decode())
client.sendall("Hippee!".encode())
client.close()
log.info("Disconnected from %s:%s", *addr)
然后我可以立即重新运行服务器。通过select()
调用,我收到以下错误:
python3.3 test.py server
Socket created...
Traceback (most recent call last):
File "test.py", line 89, in <module>
with Server(12345, False) as s:
File "test.py", line 57, in __enter__
self.sock.bind(('127.0.0.1', self.port))
OSError: [Errno 98] Address already in use
那么为什么看起来select
保持我的套接字打开,我如何确保它关闭?
答案 0 :(得分:0)
万。魔术是唯一可以看到有没有select.select()
的差异的原因。根据{{3}},即使在调用.close()
后套接字仍将继续使用的原因是TIME_WAIT
尚未过期。
解决方案是使用.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
。
我尝试了这个,但它没有用,所以我问了这个问题。后来,我意识到嘿,我知道像Flask或SimpleHTTPServer这样的东西会让你立即重启服务器。所以我this page,检查了socketserver.py
中包含的库代码。我在这里发现.setsocketopt()
的使用,但是在调用.bind()
之前调用 。
要解释used the source,让我们看看文档说的是什么?
socket.setsockopt(级别,optname,值)
设置给定套接字选项的值(参见Unix手册页setsockopt(2))。所需的符号常量在套接字模块中定义(SO_ *等)。该值可以是整数或表示缓冲区的字符串。在后一种情况下,由调用者来确保字符串包含适当的位(请参阅可选的内置模块结构,以便将C结构编码为字符串)
level
指的是您想要谈论的setsocketopt()。在这种情况下,我们不需要IP层,而是需要套接字本身。套接字选项是SO_REUSEADDR
,我们正在设置标志(值= 1)。所以在内核或驱动程序的某个地方,我们有效地说,“SHHhhhhhh ......没关系。我现在不在乎你TIME_WAIT
。我想.bind()
来无论如何。“
所以我改变了我的代码:
sock.setsocketopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', self.port))
它完美无缺。
\ O /