2014-01-10 31 views
0

我有以下代码:为什么使用`select.select`会阻止释放端口/套接字?

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是保持我的插座开放,以及如何确保它关闭?

回答

0

魔法。魔术是唯一可以看到有或没有差异的原因select.select()。根据this page,即使在调用.close()之后套接字仍将保持使用的原因是TIME_WAIT尚未过期。

解决方法是使用.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

我试过这个,但它没有工作,所以我问了这个问题。后来,我意识到嘿,我知道像Flask或SimpleHTTPServer这样的东西可以让你立即重启服务器。所以我used the source,并检查了socketserver.py中包含的库代码。在这里,我发现.setsocketopt()的使用,但呼叫之前呼叫.bind()

为了解释setsocketopt(),我们来看看这些文档是怎么说的?

socket.setsockopt(level, optname, value)
Set the value of the given socket option (see the Unix manual page setsockopt(2)). The needed symbolic constants are defined in the socket module (SO_* etc.). The value can be an integer or a string representing a buffer. In the latter case it is up to the caller to ensure that the string contains the proper bits (see the optional built-in module struct for a way to encode C structures as strings)

level是指你想谈谈level of the TCP/IP stack。在这种情况下,我们不需要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/

相关问题