Как найти локальный / эфремальный номер порта?

У меня есть клиентская программа UDP, которая использует сокеты Berkley и Winsock (в зависимости от платформы).

В основном это использует getaddrinfo(), затем socket(), затем sendto(), sendto() принимает информацию об адресе, возвращенную getaddrinfo(), Мой код выглядит так:

struct addrinfo hint;
memset(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_DGRAM;
struct addrinfo *address;
getaddrinfo("127.0.0.1", "9999", &hint, &address);

SOCKET s = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
sendto(s, "test", 4, 0, address->ai_addr, address->ai_addrlen);

У меня вопрос, когда установлен локальный / эфемерный номер порта? Это установлено с вызовом sendto()? Если я отправлю больше данных на другой сервер, sendto() повторно использовать один и тот же эфемерный номер порта? Как я могу получить эфемерный номер порта (независимо от протокола)? Я знаю, что знание этого может быть бесполезным, и NAT все равно может это изменить, но я просто пытаюсь понять, как все это работает лучше.

Я также знаю, что могу использовать bind() установить локальный порт, но мой вопрос о том, что происходит, когда ОС выбирает локальный порт для меня.

3 ответа

Вы хотите getsockname функция:

struct sockaddr_storage ss;
socklen_t len;

len = sizeof(ss);
if (getsockname(s, (struct sockaddr *)&ss, &len) == 0) {
    // print contents of ss
}

Населяет данное sockaddr с адресом и портом, к которому привязан сокет.

Эта функция доступна как в розетках winsock, так и в Berkely.

Документация MSDN для sendto()состояния:

Примечание. Если сокет открыт,setsockopt звонок сделан, а затемsendtoвызов сделан, Windows Sockets выполняет неявное bindвызов функции.

Если сокет не связан, уникальные значения присваиваются локальной ассоциацией системой, и сокет затем помечается как связанный. Если розетка подключена, getsockname Функция может быть использована для определения локального IP-адреса и порта, связанного с сокетом.

Если розетка не подключена, getsockname Функция может использоваться для определения номера локального порта, связанного с сокетом, но в качестве возвращаемого IP-адреса задается подстановочный адрес для данного протокола (например, INADDR_ANY или "0.0.0.0" для IPv4 и IN6ADDR_ANY_INIT или "::" для IPv6).

Вы можете bind в порт ноль (0) который заставит ОС найти открытый эфемерный порт, который вы можете обнаружить с помощью getsockname или вернуть EADDRINUSE еще до того, как вы пытались что-либо отправить.


Что касается того, когда операционная система выделяет временный порт, со страницы руководства ip(7) Linux:

[...] Эфемерный порт выделяется сокету при следующих обстоятельствах:

  • номер порта в адресе сокета указывается как 0 при вызове bind(2);

  • listen(2) вызывается на сокете потока, который ранее не был связан;

  • connect(2) был вызван на сокет, который ранее не был связан;

  • sendto(2) вызывается на сокете дейтаграммы, который ранее не был связан.

Другие вопросы по тегам