Как найти локальный / эфремальный номер порта? - PullRequest
0 голосов
/ 09 ноября 2018

У меня есть клиентская программа 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 ]

0 голосов
/ 09 ноября 2018

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

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

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

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

0 голосов
/ 09 ноября 2018

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


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

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

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

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

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

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

0 голосов
/ 09 ноября 2018

Вы хотите функцию 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.

...