Как мне получить бесплатный порт сокета?C++

StackOverflow https://stackoverflow.com/questions/803012

  •  03-07-2019
  •  | 
  •  

Вопрос

Я пишу тестовый клиент / сервер UDP и хочу получить его через брандмауэр.Предположительно, все, что мне нужно сделать, это заставить обе стороны отправлять сообщения на правильный IP-адрес и сервер.Получение IP-адреса не является проблемой, но как мне заставить клиента выбрать случайный свободный порт и сообщить о нем пользователю?В конечном итоге я хотел бы, чтобы он подключался к серверу matchmaker, но прямо сейчас мне нужен простой рабочий прототип, и я хотел бы указать номер порта, чтобы мой друг / тестировщик мог отправить мне # через IM, чтобы мы могли протестировать.

Как мне узнать номер порта?извините за длинный рассказ.Я замечаю, что люди говорят мне не делать того, о чем я прошу, когда я не даю описание :(

Это было полезно?

Решение

Если использовать сугубо технический термин, то на самом деле это довольно неприятная проблема или даже пара неприятных проблем.В зависимости от конфигурации брандмауэра, он обычно разрешает получать ответы от другой конечной точки на той IP-точке, с которой поступил запрос.Итак...если ваш друг получает UDP-дейтаграмму, используя что-то вроде recvfrom() при системном вызове параметр address получит информацию об IP-конечной точке для ответа.Таким образом, другой конец должен быть в состоянии ответить sendto() используя ту же адресную информацию.Что -то вроде:

/* initiator */
struct sockaddr_in hisaddr;
memset(&hisaddr, 0, sizeof(hisaddr));
hisaddr.sin_addr.s_addr = htonl(target_ip);
hisaddr.sin_port = htons(target_port);
sendto(sd, msg_ptr, msg_sz, 0, (struct sockaddr*)&hisaddr, sizeof(hisaddr));

/* receiver */
struct sockaddr_in peeraddr;
socklen_t peer_sz = sizeof(peeraddr);
recvfrom(sd, buf_ptr, buf_sz, 0, (struct sockaddr*)&peeraddr, &peer_sz);
/* build response */
sendto(sd, msg_ptr, msg_sz, 0, (struct sockaddr*)&peeraddr, peer_sz);

Тот Самый peeraddr на другой стороне будет ваш внешний адрес или, правильнее сказать, IP-адрес вашего брандмауэра и номер порта, который он решил использовать.Номер порта, который вы указываете в своем коде, может полностью отличаться от порта, на который ваш друг должен был бы отправлять данные.В конечном счете, может быть неважно, какой порт вы выберете для использования, поскольку брандмауэр может отправлять и получать сообщения на совершенно другом порту - вот что Преобразование сетевого адреса это все о чем.Я бы порекомендовал почитать RFC3235 вот несколько советов о том, как преодолеть это препятствие.

Лучший подход, ИМХО, заключается в том, чтобы:

  1. Позвольте операционной системе выбрать порт, либо вызвав bind() с нулевым номером порта или вообще без привязки
  2. Получение клиентом адресной информации с уровня сокета (например, пятого и шестого аргументов для recvfrom())
  3. Клиент отправляет ответ конечной точке, полученной на предыдущем шаге
  4. Настраивайте конфигурации брандмауэра до тех пор, пока не сработают предыдущие шаги

Конечно, вся магия заключается в последнем шаге.Если вы можете отключить NAT или убедиться, что брандмауэр никогда не будет переключать порты, то укажите номер порта и bind-обращение к нему тоже сработает.Возможно, вы захотите взглянуть на %WINDIR%\system32\drivers\etc\services (или /etc/services в зависимости от предпочтений вашей операционной системы), чтобы получить представление о том, какие номера портов зарезервированы или обычно используются.

Другие советы

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

Если я правильно понимаю ваш вопрос, я не уверен, что есть способ сделать то, что вы хотите программно (и даже если есть, я не думаю, что это правильный подход).Я думаю, вам нужно найти порт, который не используется на серверном компьютере (и, возможно, другой или тот же порт на клиентском компьютере, если связь двунаправленная), И этот порт должен иметь возможность проходить через ваш брандмауэр.Я полагаю, поскольку вы говорите "получение IP-адреса не проблема", вы уже настроили свой брандмауэр на перенаправление некоторых или всех портов на определенный компьютер внутри брандмауэра?Если это так, то порт, который вы ищете, является одним из тех, которые вы перенаправили.Вы можете просто выбрать произвольный, если на этом порту не запущена никакая другая служба.Порты ниже 1024 зарезервированы, поэтому вы, вероятно, захотите выбрать большее число, чем это.Вы можете использовать простой инструмент сканирования портов, такой как nmap - карта чтобы узнать, какие службы запущены на вашем компьютере через какие порты, и выбрать другую.Обратите внимание , что nmap может быть обманут брандмауэрами и различными bind правила при создании сокетов.

Я думаю, вам лучше выбрать фиксированный порт, а не полагаться на случайный номер порта, выбранный O / S.

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

Если вы используете WINSOCK, проверьте эту ссылку:http://msdn.microsoft.com/en-us/library/aa280717 (ПРОТИВ 60).aspx В принципе, у вас есть 2 варианта: установите порт равным 0 и позвольте системе назначить вам один или выберите случайный попробуйте открыть сокет, если он не работает, попробуйте другой (обязательно избегайте зарезервированных портов)

привяжите () сокет перед отправкой ваших данных.Укажите порт 0 для bind(), и операционная система выберет для вас неиспользуемый порт.Затем вы можете использовать getsockname(), чтобы узнать, какой порт выбран wsa.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top