На localhost, как выбрать номер свободного порта?


161

Я пытаюсь поиграть с межпроцессным взаимодействием, и так как я не мог понять, как использовать именованные каналы под Windows, я думал, что буду использовать сетевые сокеты. Все происходит локально. Сервер может запускать ведомые в отдельном процессе и прослушивает какой-либо порт. Рабы выполняют свою работу и представляют результат мастеру. Как выяснить, какой порт доступен? Я предполагаю, что не могу слушать порт 80 или 21?

Я использую Python, если это сокращает выбор.

Спасибо!


1
Кстати, если вы просто выберете случайный или случайный номер порта (желательно больше 1024), он, вероятно, будет доступен. Вы даже можете использовать порт 80 или 21 или любой другой, при условии, что никакая другая программа не слушает его. В любой момент времени в нормальной системе используется только небольшая часть портов.
Дэвид Z

19
Выбор случайного порта не очень хорошая идея - пусть ОС выберет один для вас.
Corehpf

Ответы:


225

Не привязывайте к определенному порту или привязывайте к порту 0, например sock.bind(('', 0)). Затем ОС выберет для вас доступный порт. Вы можете получить выбранный порт, используя его sock.getsockname()[1], и передать его ведомым, чтобы они могли подключиться обратно.


4
См. Stackoverflow.com/a/2838309/3538289 для примераsock.bind(('',0))
cevaris

10
Как вы передаете номер рабам? Похоже, проблема курицы и яйца для меня.
Себастьян

2
Если подчиненные устройства создаются после привязки, вы можете просто передать их в качестве параметра при их создании. В качестве альтернативы вы можете записать его в некоторую разделяемую память или файл, к которому оба могут получить доступ, или центральный сервер, к которому осуществляется доступ через некоторый хорошо известный номер порта, может отслеживать это.
mark4o

49

Ради того, что ребята объяснили выше:

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]

3
если на локальном s.bind(('localhost', 0))
хосте

3
Также полезно добавить следующее, чтобы вы могли быстро повторно использовать этот порт перед оператором return:s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
jonEbird

1
@jonEbird socket.SO_REUSEADDRДействительно помогает в этом случае? Из того, что я прочитал, имеет значение только то, что имеет сокет, который пытается связать, SO_REUSEADDRи не имеет значения, установлен ли этот флаг на длительном сокете.
Карл Бартель

41

Свяжите сокет с портом 0. Будет выбран произвольный свободный порт от 1024 до 65535. Вы можете получить выбранный порт getsockname()сразу после bind().


2

Вы можете слушать на любом порту, который вы хотите; Как правило, пользовательские приложения должны прослушивать порты 1024 и выше (через 65535). Главное, если у вас есть переменное число слушателей, это выделить диапазон для вашего приложения - скажем, 20000-21000, и CATCH EXCEPTIONS . Именно так вы узнаете, является ли порт непригодным (используется другим процессом, другими словами) на вашем компьютере.

Однако в вашем случае у вас не должно возникнуть проблем с использованием одного жестко закодированного порта для прослушивателя, если вы печатаете сообщение об ошибке в случае сбоя привязки.

Также обратите внимание, что большинство ваших сокетов (для подчиненных) не обязательно должны быть явно привязаны к определенным номерам портов - только сокеты, которые ждут входящих соединений (как, например, ваш ведущий здесь), должны быть сделаны слушателем и привязаны к порту. Если порт не указан для сокета до его использования, ОС назначит для него используемый порт. Когда мастер хочет ответить подчиненному, который отправляет ему данные, адрес отправителя становится доступным, когда слушатель получает данные.

Я полагаю, вы будете использовать UDP для этого?


0

Если вам нужно только найти свободный порт для последующего использования, вот фрагмент, похожий на предыдущий ответ , но более короткий, с использованием socketserver :

import socketserver

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

Обратите внимание, что порт не гарантированно останется свободным, поэтому вам может потребоваться поместить этот фрагмент и код, использующий его, в цикл.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.