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

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

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

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

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

вызов.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
0
15 542
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

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, «Прочитать% d байтов по локальному адресу% s \ n», nbytes, inet_ntoa (bindaddr.sin_addr.s_addr));

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

исходный адрес - это удаленный адрес

Remember Monica 21.01.2014 19:50
Ответ принят как подходящий

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

Нравиться:

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

Только если пакет исходит из подсети адаптера. Нет, если его направили туда ...

Len Holgate 17.09.2008 13:25

Ага, Лен прав. Если пакет прибыл через NAT на ваш локальный маршрутизатор, вы просто увидите свой локальный IP-адрес (что-то вроде 10.0.0.2).

Eli Bendersky 05.03.2009 20:50

Разве широковещательные рассылки не маршрутизируются (отправляются только локально)? Таким образом, метод IIRC должен работать.

Luis Soeiro 26.02.2010 16:46

Это работает для широковещательных рассылок, но широковещательные рассылки - далеко не единственные пакеты, которые может получать сервер. Это не работает для общего случая сервера, имеющего несколько IP-адресов, любой из которых может принимать пакеты из любого места в Интернете.

plugwash 14.08.2016 01:10

Доброго времени суток,

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

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

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

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

HTH.

ваше здоровье, Роб

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

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

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

Я собираюсь изучить это. Может, это лучшее решение.

Christopher 23.09.2008 16:42

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

gethostbyname("localhost");

Это позволит получить только то, что localhost настроено для разрешения (который должен быть 127.0.0.1) .. не принимающий конец пакета UDP.

Urkle 22.05.2015 21:54

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

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

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

вы можете сначала установить SO_REUSEADDR в значение true

BOOL bOptVal = 1;
setsockopt(so, SOL_SOCKET, SO_REUSEADDR, (char *)&boOptVal, sizeof(bOptVal));

после receive_from( ..., &remoteAddr, ... ); создайте еще один сокет и снова подключитесь к remoteAddr. Затем вызовите getsockname и получите IP-адрес.

SOCKET skNew = socket( )
// Same local address and port as that of your first socket 
// INADDR_ANY
bind(skNew, ,  )
// set SO_REUSEADDR to true again
setsockopt(skNew, SOL_SOCKET, SO_REUSEADDR, (char *)&boOptVal, sizeof(bOptVal));

// connect back  
connect(skNew, remoteAddr)

// get local address of the socket
getsocketname(skNew, )

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