2013-03-06 7 views
9

Oto mój kod, aby uruchomić serwer:pyton już w użyciu, ale adres tcpserver zamknąć serwer i używam `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 

Jednak to, co się dzieje:

^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 

Dlaczego ? Zamykam serwer i ustawię allow_reuse_address na True ... Using python 2.6.8.

Odpowiedz

13

Dzięki innym odpowiedziom, wymyśliłem to. allow_reuse_address powinny być na zajęciach, a nie na przykład:

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

ja nadal nie jestem pewien, dlaczego zamykając gniazdo nie uwolni go do następnego uruchomienia serwera, choć.

3

To dlatego, że 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

a nie wielki cytat. Port * nadal * występuje * w stanie TIME_WAIT. Nie ma gniazda i nie można słuchać. – EJP

+0

Rozumiem, ale chciałem wiedzieć, dlaczego moje rozwiązania (ustaw 'allow_reuse_address' i zamknij gniazdo podczas zamykania) nie naprawiły tego. – Claudiu

0

To dlatego, że trzeba ustawić SO_REUSEADDRESS przed powiązać gniazda. Podczas tworzenia i wiązania gniazda w jednym kroku, a następnie ustawiania, jest już za późno.

+0

Ach, więc 'allow_reuse_address' zasadniczo nie robi nic dla domyślnej implementacji' TCPServer'? Czy zamknięcie gniazda na końcu programu nie powinno również zapobiegać temu błędowi? – Claudiu

3

The [Err 98] Address already in use Wynika to z faktu, gniazdo było .close() ale nadal czeka na wystarczająco dużo czasu, aby przejść do upewnij się, że zdalne TCP otrzymał potwierdzenie wniosku o zerwanie połączenia (patrz TIME_WAIT). Domyślnie nie są dopuszczone do związania gniazdo jeśli gniazdo zobowiązany do tego portu, ale można zastąpić że z allow_reuse_address (SO_REUSEADDR)

Chociaż można zmutować TCPServer.allow_reuse_addr (jak zaproponowano w this other answer), myślę, że jest bardziej czyste do własnej podklasy TCPServer gdzie allow_reuse_address jest ustawiony na 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 

można użyć zdecydowanie ustalonym allow_reuse_address na samej instancji (nie brudząc z klas), ale trzeba użyć TCPServer(..., bind_and_activate=False), inaczej gniazdo będzie zobowiązany przed masz szansę zmień ustawienie allow_reuse_address. Następnie trzeba ręcznie wywołać .server_bind() i .server_activate() przed 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() 
Powiązane problemy