Как получить свой (локальный) IP-адрес из udp-сокета (C/C++)
Вопрос
- У вас есть несколько сетевых адаптеров.
- Привяжите сокет UDP к локальному порту без указания адреса.
- Получайте пакеты на одном из адаптеров.
Как получить локальный 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.
Так что же вы можете с этим поделать?
- Можно догадаться (например на основании таблицы маршрутизации).
- Вы можете получить список локальных адресов и привязать отдельный сокет к каждому локальному адресу.
- Вы можете использовать более новые 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");