2013-03-06 18 views
9

Ecco il mio codice per eseguire il server:pitone indirizzo TCPServer già in uso, ma ho chiudere il server e lo uso `allow_reuse_address`

class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 
    #.... 

PORT = 8089 

httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler) 
httpd.allow_reuse_address = True 

print "Serving forever at port", PORT 
try: 
    httpd.serve_forever() 
except: 
    print "Closing the server." 
    httpd.server_close() 
    raise 

Eppure questo è ciò che accade:

^CClosing the server. 
Traceback (most recent call last): 
    File "server.py", line 118, in <module> 
    self.send_error(400, "Unimplemented GET command: %s" % (self.path,)) 
    File "/home/claudiu/local/lib/python2.6/SocketServer.py", line 224, in serve_forever 
    r, w, e = select.select([self], [], [], poll_interval) 
KeyboardInterrupt 
(.virtualenv)[email protected]:~/xxx$ python server.py 
Traceback (most recent call last): 
    File "server.py", line 122, in <module> 
    httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler) 
    File "/home/claudiu/local/lib/python2.6/SocketServer.py", line 402, in __init__ 
    self.server_bind() 
    File "/home/claudiu/local/lib/python2.6/SocketServer.py", line 413, in server_bind 
    self.socket.bind(self.server_address) 
    File "<string>", line 1, in bind 
socket.error: [Errno 98] Address already in use 

Perché ? Chiudo il server e imposta allow_reuse_address su True ... utilizzando python 2.6.8.

risposta

13

Grazie alle altre risposte, l'ho capito. allow_reuse_address dovrebbe essere sulla classe, non sulla istanza:

SocketServer.TCPServer.allow_reuse_address = True 
httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler) 

io non sono ancora sicuro perché chiudendo la presa non liberarlo per la successiva esecuzione del server, però.

3

È perché TCP TIME_WAIT.

Somebody discovered this exact problem.

However, if I try to stop and start the server again to test any modifications, I get a random “socket.error: [Errno 98] Address already in use” error. This happens only if a client has already connected to the server.

Checking with netstat and ps, I found that although the process it self is no longer running, the socket is still listening on the port with status “TIME_WAIT”. Basically the OS waits for a while to make sure this connection has no remaining packets on the way.

+0

Non una grande citazione. La * porta * è ancora * presente * nello stato TIME_WAIT. Non c'è presa né ascolto. – EJP

+0

Capisco, ma volevo sapere perché le mie soluzioni (impostare 'allow_reuse_address' e chiudere il socket su shutdown) non lo risolvevano. – Claudiu

0

E 'perché è necessario impostare SO_REUSEADDRESS prima si associa il socket. Mentre stai creando e legando il socket tutto in un solo passaggio e quindi impostandolo, è già troppo tardi.

+0

Ah quindi 'allow_reuse_address' praticamente non fa nulla per l'implementazione' TCPServer' di default? Inoltre non dovrebbe chiudere il socket alla fine del programma per prevenire questo errore? – Claudiu

3

Il [Err 98] Address already in use è dovuto al fatto che la presa era .close() ma è ancora in attesa per un tempo sufficiente a passare per essere sicuri che il TCP remota ha ricevuto il riconoscimento della sua richiesta di cessazione di connessione (vedi TIME_WAIT). Per impostazione predefinita, non ti è permesso di legare una presa se c'è un legame socket con questo porto, ma è possibile escludere che con allow_reuse_address (SO_REUSEADDR)

Anche se è possibile mutare TCPServer.allow_reuse_addr (come proposto nella this other answer), credo sia più pulita alla propria sottoclasse di TCPServer dove allow_reuse_address è impostato su True:

import SocketServer 
import SimpleHTTPServer 
import time 

class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 
    def do_GET(): 
     time.sleep(60) 
     self.request.sendall("I'm here!") 

class ReuseAddrTCPServer(SocketServer.TCPServer): 
    allow_reuse_address = True 

PORT = 8089 

httpd = ReuseAddrTCPServer(("", PORT), MyRequestHandler) 
httpd.daemon_threads = True 


print "Serving forever at port", PORT 
try: 
    httpd.serve_forever() 
except: 
    print "Closing the server." 
    httpd.server_close() 
    raise 

È possibile utilizzare sicuramente impostare allow_reuse_address sulla istanza stessa (senza fare confusione con le classi), ma è necessario utilizzare TCPServer(..., bind_and_activate=False), altrimenti la presa sarà legato prima hai la possibilità di modificare l'impostazione allow_reuse_address. Quindi è necessario chiamare manualmente .server_bind() e .server_activate() prima serve_forever():

... 
httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler, bind_and_activate=False) 
httpd.allow_reuse_address = True 
httpd.daemon_threads = True 
... 
httpd.server_bind() 
httpd.server_activate() 
httpd.serve_forever() 
Problemi correlati