#sockets #boost-asio #winsock
#сокеты #boost-asio #winsock
Вопрос:
У меня есть UDP-сервер, который прослушивает все адреса IPv4, то есть INADDR_ANY или 0.0.0.0. На каждый пакет, который он получает от клиента, он должен отвечать IP-адресом точного сетевого интерфейса, который получил пакет. Концептуально он должен помещать адрес источника пакета в полезную нагрузку пакета.
Как я узнаю, какой адрес источника будет использоваться для отправки пакета на определенный адрес назначения при использовании sendto()
? Нужно ли мне перечислять все сетевые интерфейсы вручную, сопоставляя сетевые префиксы (или IPv4-адреса с маской подсети, если на то пошло) с адресом назначения? Если да, то как? Меня бы заинтересовало кроссплатформенное решение, включающее Boost.Asio но если это невозможно, мне нужно использовать Winsock. В будущем мне может понадобиться решение и для Linux.
Ответ №1:
На каждый пакет, который он получает от клиента, он должен отвечать IP-адресом точного сетевого интерфейса, который получил пакет.
Обычно это не то, что вам нужно обрабатывать вручную. Если вы просто sendto()
отправили пакет на IP / порт отправителя, который recvfrom()
сообщил, ОС направит пакет для вас, основываясь на том, какой интерфейс имеет доступный маршрут к этому отправителю.
Как я узнаю, какой адрес источника будет использоваться для отправки пакета на определенный адрес назначения при использовании
sendto()
?
Вы можете использовать setsockopt()
для включения IP_PKTINFO
опции в прослушивающем сокете UDP, а затем использовать WSARecvMsg()
(Windows) или recvmsg()
(Linux) вместо recvfrom()
. Это даст вам доступ к in_pktinfo
структуре, которая сообщает вам IP-адрес назначения, на который был отправлен полученный пакет, и какой интерфейс фактически его получил.
Комментарии:
1. Это отличная новость! Я не знал о
recvmsg()
/WSARecvMsg()
. Я предполагаю, что это также будет работать с сокетом, отличным от IPV6_V6ONLY, и возвращать адреса, сопоставленные с IPv4, верно? Причина, по которой я это делаю, заключается в поддержке какого-то дерьмового протокола, иначе я бы использовал толькоrecvfrom()
andsendto()
, как вы предложили.2. @purefanatic для сокета IPv6 (независимо
IPV6_V6ONLY
от того, включен он у вас или нет), вам необходимо включить опциюIPV6_PKTINFO
(Windows) илиIPV6_RECVPKTINFO
(Linux), а затемWSARecvMsg()
/recvmsg()
предоставит вамin6_pktinfo
struct вместоin_pktinfo
.3. Я вижу. Я забыл упомянуть об одном случае использования: клиент может использовать широковещательный адрес для доступа к серверу. В этом случае сервер все равно должен отправить одноадресный ответ, но информация, полученная из
recvmsg()
, будет не очень полезной, поскольку она вернет исходный широковещательный адрес или нет?4. @purefanatic Это даст вам используемый широковещательный IP-адрес, да. Но это все равно даст вам конкретный интерфейс, на котором была получена широковещательная передача, и у вас все равно будет IP / порт отправителя. Таким образом, при необходимости вы можете привязать отдельный сокет к этому интерфейсу и использовать его для отправки обратно этому отправителю. В противном случае, как я уже сказал, просто отправьте отправителю обратно, используя принимающий сокет, и пусть ОС обрабатывает маршрутизацию за вас.