为什么使用`select.select`会阻止释放端口/套接字?

时间:2014-01-10 13:21:51

标签: python sockets

我有以下代码:

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.selectsetblocking(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保持我的套接字打开,我如何确保它关闭?

1 个答案:

答案 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 /