Сведения о вопросе

HEIGTH

19:59, 24th August, 2020

Теги

c++   sockets   udp    

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

Просмотров: 524   Ответов: 6

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

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

Вопрос в том, "What is the ip address from the receiver adapter?" не адрес от отправителя, который мы получаем в

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

вызов.



  Сведения об ответе

lourence

00:00, 2nd August, 2020

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

Нравится:

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


  Сведения об ответе

+-*/

07:42, 27th August, 2020

Добрый день,

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

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

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

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

HTH.

овации, Грабить


  Сведения об ответе

P_S_S

04:50, 19th August, 2020

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

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

Исходный код доступен здесь . Рассматриваемая функция - 'recvfrom_flags()'


  Сведения об ответе

DINO

05:46, 22nd August, 2020

К сожалению, вызовы sendto и recvfrom API принципиально нарушаются при использовании с сокетами, привязанными к "Any IP", потому что у них нет поля для локальной информации IP.

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

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

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

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


  Сведения об ответе

ASER

15:58, 22nd August, 2020

Попробовать это:

gethostbyname("localhost");


  Сведения об ответе

FAriza

14:26, 15th August, 2020

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));


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться