Как получить свой (локальный) IP-адрес из udp-сокета (C/C++)

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

  •  09-06-2019
  •  | 
  •  

Вопрос

  1. У вас есть несколько сетевых адаптеров.
  2. Привяжите сокет UDP к локальному порту без указания адреса.
  3. Получайте пакеты на одном из адаптеров.

Как получить локальный IP-адрес адаптера, получившего пакет?

Вопрос в том, что "что такое IP -адрес с адаптера приемника?" не адрес от отправителя, который мы получаем в

receive_from( ..., &senderAddr, ... );

вызов.

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

Решение

Вы можете перечислить все сетевые адаптеры, получить их IP-адреса и сравнить часть, покрытую маской подсети, с адресом отправителя.

Нравиться:

IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
{
    foreach( adapter in EnumAllNetworkAdapters() )
    {
        adapterSubnet = adapter.subnetmask & adapter.ipaddress;
        senderSubnet = adapter.subnetmask & senderAddr;
        if( adapterSubnet == senderSubnet )
        {
            return adapter.ipaddress;
        }
    }
}

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

Добрый день,

Я предполагаю, что вы выполнили привязку, используя INADDR_ANY для указания адреса.

Если это так, то семантика INADDR_ANY такова, что сокет UDP создается на порту, указанном на всех ваших интерфейсах.Сокет будет получать все пакеты, отправленные на все интерфейсы указанного порта.

При отправке с использованием этого сокета используется интерфейс с наименьшим номером.В поле адреса исходящего отправителя указывается IP-адрес первого используемого исходящего интерфейса.

Первый исходящий интерфейс определяется как последовательность действий при выполнении команды ifconfig -a.Вероятно, это будет eth0.

ХТХ.

Ура, Роб

Решение, предоставленное тимбо предполагает, что диапазоны адресов уникальны и не перекрываются.Хотя обычно это так, это не универсальное решение.

Существует превосходная реализация функции, которая выполняет именно то, что вы предоставили в книге Стивена «Сеть программирования Unix» (раздел 20.2) Это функция, основанная на recvmsg (), а не recvfrom ().Если в вашем сокете включена опция IP_RECVIF, функция Recvmsg() вернет индекс интерфейса, на котором был получен пакет.Затем это можно использовать для поиска адреса назначения.

Исходный код доступен здесь.Речь идет о функции «recvfrom_flags()».

К сожалению, вызовы API sendto и Recvfrom принципиально не работают при использовании сокетов, привязанных к «Любому IP», поскольку у них нет поля для информации о локальном IP.

Так что же вы можете с этим поделать?

  1. Можно догадаться (например на основании таблицы маршрутизации).
  2. Вы можете получить список локальных адресов и привязать отдельный сокет к каждому локальному адресу.
  3. Вы можете использовать более новые API, поддерживающие эту информацию.Это состоит из двух частей: во-первых, вам нужно использовать опцию сокета relavent (ip_recvif для IPv4, ipv6_recvif для IPv6), чтобы сообщить стеку, что вам нужна эта информация.Затем вам придется использовать другую функцию (recvmsg в Linux и некоторых других unix-подобных системах, WSArecvmsg в Windows) для получения пакета.

Ни один из этих вариантов не является отличным.Очевидно, что догадки иногда приводят к неверным ответам.Привязка отдельных сокетов увеличивает сложность вашего программного обеспечения и вызывает проблемы, если список локальных адресов изменится во время работы вашей программы.Новые API являются правильным техническим решением, но могут снизить переносимость. (в частности, похоже, что WSArecvmsg недоступен в Windows XP) и может потребоваться внести изменения в используемую вами библиотеку-оболочку сокетов.

Похоже, что я ошибался, похоже, что документация MS вводит в заблуждение и что WSArecvmsg доступен в Windows XP.Видеть https://stackoverflow.com/a/37334943/5083516

ssize_t
     recvfrom(int socket, void *restrict buffer, size_t length, int flags,
         struct sockaddr *restrict address, socklen_t *restrict address_len);

     ssize_t
     recvmsg(int socket, struct msghdr *message, int flags);

[..]
     If address is not a null pointer and the socket is not connection-oriented, the
     source address of the message is filled in.

Фактический код:

int nbytes = recvfrom(sock, buf, MAXBUFSIZE, MSG_WAITALL, (struct sockaddr *)&bindaddr, &addrlen);

fprintf(stdout, "Read %d bytes on local address %s\n", nbytes, inet_ntoa(bindaddr.sin_addr.s_addr));

надеюсь это поможет.

Попробуй это:

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