Как получить свой собственный (локальный) IP-адрес из udp-сокета (C / C ++) - PullRequest
10 голосов
/ 27 августа 2008
  1. У вас есть несколько сетевых адаптеров.
  2. Привязать сокет UDP к локальному порту без указания адреса.
  3. Получать пакеты на один из адаптеров.

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

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

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

вызов.

Ответы [ 6 ]

3 голосов
/ 20 сентября 2008

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

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

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

3 голосов
/ 27 августа 2008

G'day,

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

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

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

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

НТН.

веселит, Rob

3 голосов
/ 27 августа 2008

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

Как:

IPAddress FindLocalIPAddressOfIncomingPacket( senderAddr )
{
    foreach( adapter in EnumAllNetworkAdapters() )
    {
        adapterSubnet = adapter.subnetmask & adapter.ipaddress;
        senderSubnet = adapter.subnetmask & senderAddr;
        if( adapterSubnet == senderSubnet )
        {
            return adapter.ipaddress;
        }
    }
}
0 голосов
/ 14 августа 2016

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

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

  1. Вы можете угадать (например, на основе таблицы маршрутизации).
  2. Вы можете получить список локальных адресов и привязать отдельный сокет к каждому локальному адресу.
  3. Вы можете использовать более новые API, которые поддерживают эту информацию. Это состоит из двух частей: во-первых, вы должны использовать опцию relayvent socket (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

0 голосов
/ 16 октября 2010

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

gethostbyname("localhost");
0 голосов
/ 27 августа 2008
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);</p> <p>fprintf(stdout, "Read %d bytes on local address %s\n", nbytes, inet_ntoa(bindaddr.sin_addr.s_addr));

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...