Як я можу вибрати номер безкоштовного порту на localhost?


161

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

Я використовую Python, якщо це зменшує вибір.

Дякую!


1
До речі, якщо ви просто виберете випадковий чи випадковий номер порту (бажано вище 1024), він, ймовірно, буде доступний. Ви навіть можете використовувати порт 80 або 21 чи будь-що інше, поки жодна інша програма не слухає його. У будь-який час у звичайній системі використовується лише невелика частина портів.
David 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
якщо на localhost: можливо s.bind(('localhost', 0)), краще
codekyblue

3
Також добре додати наступне, щоб ви могли швидко скористатися цим портом перед своїм поверненням: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 ВИКЛЮЧЕННЯ . Саме так ви дізнаєтесь, чи не використовується порт, який використовується у іншому процесі (іншими словами) на вашому комп'ютері.

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

Зауважте також, що більшість ваших сокетів (для рабів) не потрібно чітко прив’язувати до певних номерів портів - лише розетки, які чекають вхідних з'єднань (як ваш головний господар тут), потрібно зробити слухачем і прив’язати до порту. Якщо порт не вказаний для сокета перед його використанням, ОС призначить порт, який може бути використаний. Коли ведучий хоче відповісти підлеглому, який надсилає йому дані, адреса відправника стає доступною, коли слухач отримує дані.

Я припускаю, що ви будете використовувати для цього 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.