Mam pytanie dotyczące gniazda klienta w sieci TCP / IP. Powiedzmy, że używam
try:
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error, msg:
sys.stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(1)
try:
comSocket.bind(('', 5555))
comSocket.connect()
except socket.error, msg:
sys.stderr.write("[ERROR] %s\n" % msg[1])
sys.exit(2)
Utworzone gniazdo będzie powiązane z portem 5555. Problem w tym, że po zakończeniu połączenia
comSocket.shutdown(1)
comSocket.close()
Używając wireshark, widzę, że gniazdo jest zamknięte FIN, ACK i ACK z obu stron, nie mogę ponownie użyć portu. Otrzymuję następujący błąd:
[ERROR] Address already in use
Zastanawiam się, jak mogę od razu wyczyścić port, aby następnym razem nadal korzystać z tego samego portu.
comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Program setsockopt nie jest w stanie rozwiązać problemu. Dziękujemy!
TIME_WAIT
stanem gniazda, na który odpowiedź poniżej ma rozwiązanie :)Odpowiedzi:
Spróbuj użyć
SO_REUSEADDR
opcji gniazda przed powiązaniem gniazda.comSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Edycja: widzę, że nadal masz z tym problemy. Jest przypadek, w którym
SO_REUSEADDR
nie zadziała. Jeśli spróbujesz powiązać gniazdo i ponownie połączyć się z tym samym miejscem docelowym (zSO_REUSEADDR
włączoną opcją), toTIME_WAIT
nadal będzie obowiązywać. Pozwoli to jednak na połączenie z innym hostem: portem.Przychodzi mi na myśl kilka rozwiązań. Możesz kontynuować ponawianie, aż ponownie uzyskasz połączenie. Lub jeśli klient zainicjuje zamknięcie gniazda (nie serwera), powinno to magicznie działać.
źródło
setsockopt
wcześniejbind
? czy pierwsze gniazdo zostało utworzone za pomocąSO_REUSEADDR
, czy tylko nieudane? gniazdo oczekujące musi byćSO_REUSEADDR
ustawione, aby to działałobind()
, ale SO_REUSEADDR jest dla wszystkich gniazd, w tym nie tylko gniazd serwera TCP, ale także gniazd klienta TCP i gniazd UDP. Nie publikuj tutaj dezinformacji.Oto pełny kod, który przetestowałem i absolutnie NIE daje mi błędu „adres już używany”. Możesz zapisać to w pliku i uruchomić plik z katalogu podstawowego plików HTML, które chcesz udostępniać. Ponadto można było programowo zmieniać katalogi przed uruchomieniem serwera
import socket import SimpleHTTPServer import SocketServer # import os # uncomment if you want to change directories within the program PORT = 8000 # Absolutely essential! This ensures that socket resuse is setup BEFORE # it is bound. Will avoid the TIME_WAIT issue class MyTCPServer(SocketServer.TCPServer): def server_bind(self): self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) Handler = SimpleHTTPServer.SimpleHTTPRequestHandler httpd = MyTCPServer(("", PORT), Handler) # os.chdir("/My/Webpages/Live/here.html") httpd.serve_forever() # httpd.shutdown() # If you want to programmatically shut off the server
źródło
Jeśli napotkasz problem przy użyciu atrybutu
TCPServer
orSimpleHTTPServer
, overrideSocketServer.TCPServer.allow_reuse_address
(python 2.7.x) lubsocketserver.TCPServer.allow_reuse_address
(python 3.x)class MyServer(SocketServer.TCPServer): allow_reuse_address = True server = MyServer((HOST, PORT), MyHandler) server.serve_forever()
źródło
Zgodnie z tym linkiem
Można jednak całkowicie uniknąć stanu TIME_WAIT, upewniając się, że zdalny koniec inicjuje zamknięcie (zdarzenie zamknięcia). Serwer może więc uniknąć problemów, pozwalając klientowi zamknąć się jako pierwszy. Protokół aplikacji musi być zaprojektowany tak, aby klient wiedział, kiedy zamknąć. Serwer może bezpiecznie zamknąć się w odpowiedzi na EOF od klienta, jednak będzie musiał również ustawić limit czasu, gdy oczekuje EOF w przypadku, gdy klient opuścił sieć niezręcznie. W wielu przypadkach wystarczy odczekać kilka sekund przed zamknięciem serwera.
Radzę również dowiedzieć się więcej o sieci i programowaniu sieciowym. Powinieneś teraz przynajmniej wiedzieć, jak działa protokół tcp. Protokół jest dość trywialny i niewielki, dzięki czemu może zaoszczędzić wiele czasu w przyszłości.
Za pomocą
netstat
polecenia możesz łatwo zobaczyć, które programy ((nazwa_programu, pid) krotka) są powiązane z którymi portami i jaki jest aktualny stan gniazda: CZAS_WAIT, ZAMKNIĘCIE, FIN_WAIT i tak dalej.Naprawdę dobre wyjaśnienie konfiguracji sieci linuxa można znaleźć /server/212093/how-to-reduce-number-of-sockets-in-time-wait .
źródło
Musisz ustawić allow_reuse_address przed powiązaniem. Zamiast SimpleHTTPServer uruchom ten fragment:
Handler = SimpleHTTPServer.SimpleHTTPRequestHandler httpd = SocketServer.TCPServer(("", PORT), Handler, bind_and_activate=False) httpd.allow_reuse_address = True httpd.server_bind() httpd.server_activate() httpd.serve_forever()
Zapobiega to wiązaniu się serwera, zanim mieliśmy szansę ustawić flagi.
źródło
Jak wspomniał Felipe Cruze, przed związaniem należy ustawić SO_REUSEADDR. Znalazłem rozwiązanie na innej stronie - rozwiązanie na innej stronie, przedstawione poniżej
źródło
Znalazłem inny powód tego wyjątku. Podczas uruchamiania aplikacji ze Spyder IDE (w moim przypadku był to Spyder3 na Raspbian) i programu zakończonego przez ^ C lub wyjątek, gniazdo było nadal aktywne:
sudo netstat -ap | grep 31416 tcp 0 0 0.0.0.0:31416 0.0.0.0:* LISTEN 13210/python3
Uruchomienie programu ponownie znalazło "Adres już używany"; IDE wydaje się uruchamiać nowe „uruchomienie” jako oddzielny proces, który znajduje gniazdo używane przez poprzednie „uruchomienie”.
socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
nie pomogło.
Pomógł proces zabijania 13210. Uruchamianie skryptu w Pythonie z wiersza poleceń, na przykład
zawsze działało dobrze, gdy SO_REUSEADDR było ustawione na true. Nowy Thonny IDE lub Idle3 IDE nie miał tego problemu.
źródło
socket.socket()
powinien działać przedsocket.bind()
i używaćREUSEADDR
zgodnie z opisemźródło
Dla mnie lepszym rozwiązaniem było to. Ponieważ inicjatywa zamknięcia połączenia została podjęta przez serwer,
setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
nie przyniosło to żadnego skutku, a TIME_WAIT unikał nowego połączenia na tym samym porcie z błędem:[Errno 10048]: Address already in use. Only one usage of each socket address (protocol/IP address/port) is normally permitted
Ostatecznie skorzystałem z rozwiązania, aby pozwolić systemowi operacyjnemu wybrać sam port, a następnie używany jest inny port, jeśli precedens jest nadal w TIME_WAIT.
Wymieniłem:
z:
self._socket.bind((guest, 0))
Jak wskazano w dokumentacji gniazda Pythona adresu tcp:
źródło
Innym rozwiązaniem, oczywiście w środowisku programistycznym, jest na przykład zabijanie procesu przy jego użyciu
def serve(): server = HTTPServer(('', PORT_NUMBER), BaseHTTPRequestHandler) print 'Started httpserver on port ' , PORT_NUMBER server.serve_forever() try: serve() except Exception, e: print "probably port is used. killing processes using given port %d, %s"%(PORT_NUMBER,e) os.system("xterm -e 'sudo fuser -kuv %d/tcp'" % PORT_NUMBER) serve() raise e
źródło
Wiem, że już zaakceptowałeś odpowiedź, ale wydaje mi się, że problem jest związany z wywołaniem funkcji bind () na gnieździe klienta. To może być w porządku, ale bind () i shutdown () nie wydają się dobrze ze sobą współpracować. Ponadto, SO_REUSEADDR jest zwykle używany z gniazdami nasłuchującymi. tj. po stronie serwera.
Powinieneś przejść i ip / port, aby się połączyć (). Lubię to:
comSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) comSocket.connect(('', 5555))
Nie wywołuj bind (), nie ustawiaj SO_REUSEADDR.
źródło
bind()
ishutdown()
nie mają ze sobą nic wspólnego, a sugestia, że „nie wydają się dobrze ze sobą grać” jest bezpodstawna. Przegapiłeś część, w której musi użyć lokalnego portu 5555.Myślę, że najlepszym sposobem jest po prostu zabicie procesu na tym porcie, wpisując w terminalu
fuser -k [PORT NUMBER]/tcp
npfuser -k 5001/tcp
.źródło