Если использовать высокотехнологичный термин, это на самом деле довольно неприятная проблема или даже пара проблемных проблем. В зависимости от конфигурации брандмауэра он обычно разрешает ответы от другой конечной точки на конечной точке 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 , чтобы узнать, как преодолеть это препятствие.
Наилучший подход ИМХО это:
- Пусть ОС выбирает порт, либо набрав
bind()
с нулевым номером порта, либо вообще пропустив привязку
- Получение клиентом информации об адресе от уровня сокетов (например, пятый и шестой аргументы
recvfrom()
)
- Клиент отправляет ответ конечной точке, полученной на предыдущем шаге
- Изменять настройки брандмауэра, пока не сработают предыдущие шаги
Конечно, вся магия находится на последнем шаге. Если вы можете отключить NAT или убедиться, что брандмауэр никогда не будет переключать порты, то также будет работать приближение номера порта и bind
. Возможно, вы захотите взглянуть на %WINDIR%\system32\drivers\etc\services
(или /etc/services
в зависимости от склонности вашей ОС), чтобы получить представление о том, какие номера портов зарезервированы или обычно используются.