Na hoście lokalnym, jak wybrać bezpłatny numer portu?

161

Próbuję bawić się komunikacją między procesami, a ponieważ nie mogłem wymyślić, jak używać nazwanych potoków w systemie Windows, pomyślałem, że użyję gniazd sieciowych. Wszystko dzieje się lokalnie. Serwer może uruchamiać slave'ów w oddzielnym procesie i nasłuchiwać na jakimś porcie. Niewolnicy wykonują swoją pracę i przekazują wynik mistrzowi. Jak sprawdzić, który port jest dostępny? Zakładam, że nie mogę nasłuchiwać na porcie 80 lub 21?

Używam Pythona, jeśli to ogranicza wybór.

Dzięki!

Anton L.
źródło
1
Nawiasem mówiąc, jeśli po prostu wybierzesz losowy lub losowy numer portu (najlepiej wyższy niż 1024), prawdopodobnie będzie dostępny. Możesz nawet użyć portu 80 lub 21 lub innego, o ile żaden inny program na nim nie nasłuchuje. W dowolnym momencie w normalnym systemie używany jest tylko niewielki odsetek portów.
David Z
19
Wybór losowego portu nie jest dobrym pomysłem - niech system operacyjny wybierze jeden za Ciebie.
Corehpf
Na POSIX: stackoverflow.com/questions/913501/…
Ciro Santilli 郝海东 冠状 病 六四 事件 事件

Odpowiedzi:

225

Nie łącz się z określonym portem ani nie łącz z portem 0, np sock.bind(('', 0)). System operacyjny wybierze wtedy dostępny port. Możesz pobrać wybrany port sock.getsockname()[1]i przekazać go niewolnikom, aby mogli się z powrotem połączyć.

mark4o
źródło
4
Zobacz stackoverflow.com/a/2838309/3538289, aby sock.bind(('',0))
zapoznać się
10
Jak przekazujesz tę liczbę niewolnikom? Dla mnie brzmi to jak problem z kura i jajkiem.
Sebastian,
2
Jeśli slave są tworzone po powiązaniu, możesz po prostu przekazać to jako parametr podczas ich tworzenia. Alternatywnie możesz zapisać go w jakiejś pamięci współdzielonej lub pliku, do którego oboje mają dostęp, lub centralny serwer, do którego można uzyskać dostęp przez dobrze znany numer portu, mógłby to śledzić.
mark4o
49

Ze względu na fragment tego, co chłopaki wyjaśnili powyżej:

import socket
from contextlib import closing

def find_free_port():
    with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
        s.bind(('', 0))
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        return s.getsockname()[1]
saaj
źródło
3
jeśli na lokalnym hoście: może s.bind(('localhost', 0))jest lepiej
codkyblue
3
Dobrze jest również dodać następujące informacje, aby móc szybko ponownie użyć tego portu przed instrukcją powrotu:s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
jonEbird
1
@jonEbird Czy socket.SO_REUSEADDRnaprawdę pomaga w tym przypadku? Z tego, co przeczytałem, istotne jest tylko to, że gniazdo, które próbuje się połączyć, ma SO_REUSEADDRi nie ma znaczenia, czy ta flaga jest ustawiona na przedłużającym się gnieździe.
Karl Bartel
41

Powiąż gniazdo z portem 0. Zostanie wybrany losowy wolny port od 1024 do 65535. Możesz pobrać wybrany port getsockname()zaraz po bind().

Havenard
źródło
2

Możesz słuchać na dowolnym porcie; generalnie aplikacje użytkownika powinny nasłuchiwać na portach 1024 i nowszych (do 65535). Najważniejszą rzeczą, jeśli masz zmienną liczbę słuchaczy, jest przydzielenie zakresu do aplikacji - powiedzmy 20000-21000 i CATCH EXCEPTIONS . Dzięki temu dowiesz się, czy port nie nadaje się do użytku (innymi słowy jest używany przez inny proces) na Twoim komputerze.

Jednak w twoim przypadku nie powinieneś mieć problemu z używaniem pojedynczego portu zakodowanego na stałe dla twojego słuchacza, o ile drukujesz komunikat o błędzie, jeśli wiązanie się nie powiedzie.

Zauważ również, że większość twoich gniazd (dla slaveów) nie musi być jawnie powiązana z określonymi numerami portów - tylko gniazda, które czekają na połączenia przychodzące (takie jak twój master tutaj) będą musiały zostać ustawione jako nasłuchujące i powiązane z portem. Jeśli port nie zostanie określony dla gniazda przed jego użyciem, system operacyjny przypisze użyteczny port do gniazda. Kiedy master chce odpowiedzieć slave'owi, który przesyła mu dane, adres nadawcy jest dostępny, gdy słuchacz otrzyma dane.

Zakładam, że będziesz używać do tego UDP?

Walt W.
źródło
0

Jeśli potrzebujesz tylko znaleźć wolny port do późniejszego wykorzystania, oto fragment podobny do poprzedniej odpowiedzi , ale krótszy, przy użyciu serwera gniazd :

import socketserver

with socketserver.TCPServer(("localhost", 0), None) as s:
    free_port = s.server_address[1]

Pamiętaj, że nie gwarantuje się, że port pozostanie wolny, więc może być konieczne umieszczenie tego fragmentu kodu i używającego go kodu w pętli.

Mihai Capotă
źródło