Как получить бесплатный порт сокета? C ++ - PullRequest
4 голосов
/ 29 апреля 2009

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

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

Ответы [ 6 ]

5 голосов
/ 29 апреля 2009

Если использовать высокотехнологичный термин, это на самом деле довольно неприятная проблема или даже пара проблемных проблем. В зависимости от конфигурации брандмауэра он обычно разрешает ответы от другой конечной точки на конечной точке 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-адресом вашего брандмауэра и номером порта, который он выбрал для использования. Номер порта, который вы указываете в своем коде, может полностью отличаться от порта, на который ваш друг должен будет отправлять данные. В конечном счете, может не иметь значения, какой порт вы выберете, поскольку брандмауэр может отправлять и получать данные на совершенно другом порту - вот что такое Network Address Translation . Я бы порекомендовал прочитать RFC3235 , чтобы узнать, как преодолеть это препятствие.

Наилучший подход ИМХО это:

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

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

0 голосов
/ 10 июля 2009

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

0 голосов
/ 29 апреля 2009

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

0 голосов
/ 29 апреля 2009

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

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

0 голосов
/ 29 апреля 2009

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

0 голосов
/ 29 апреля 2009

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

...