2014-01-10 29 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/

相關問題