Подтвердить что ты не робот

API с использованием sockaddr_storage

Я пытаюсь сделать какое-то IP-агностическое кодирование, и, как было предложено различными источниками, я пытался использовать sockaddr_storage. Однако все вызовы API (getaddrinfo, getnameinfo) по-прежнему зависят от структуры sockaddr. И кастинг между ними не совсем хороший вариант, gves поднимаются на множество других проблем.

И приведение к sockaddr_in и sockaddr_in6 разного рода поражений, которые я пытаюсь использовать sockaddr_storage.

Любой, кто эффективно использовал sockaddr_storage в разработке простого приложения сокета для клиентских серверов.

4b9b3361

Ответ 1

Проблема совместного программирования IPV6 и IPV4 заключается в том, что сама чистая структура sockaddr недостаточно велика для хранения sockaddr_in6. Поэтому, если вам нужно слепо обходить адрес, который может быть либо sockaddr_in, либо sockaddr_in6, sockaddr_storage немного проще в использовании.

В конце дня, независимо от того, используете ли вы sockaddr_in, sockaddr_in6 или sockaddr_storage, вам нужно будет указать эти указатели, чтобы сделать вызов sendto, recvfrom, connect, accept и многих других функций сокетов. Это всего лишь известный нюанс программирования сокетов. Просто отпустите чувство что-то небезопасное. Ваш код будет в порядке.

Теперь, когда вы пишете сетевой код, предназначенный для работы как для IPV4, так и для IPV6, вы можете легко попасть в ловушку наличия множества операторов switch для обработки разных типов сетей. Затем код становится беспорядочным, как показано ниже:

if (addr.ss_family == AF_INET)
    sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in))
else (addr.ss_family == AF_INET6)
    sendto(sock, buffer, len, 0, (sockaddr*)&addr, sizeof(sockaddr_in6));

И тогда этот тип выражения "if family == AF_INET" может легко начинать повторяться снова и снова. Это то, чего вы хотите избежать.

Предполагая, что вы используете С++, вы обнаружите, что класс абстракции для объекта сокета-адреса невероятно полезен. У меня есть пример на github здесь и здесь. Класс CSocketAddress поддерживается объединением {sockaddr, sockaddr_in, sockaddr_in6} и может быть построен с помощью sockaddr_storage. Если бы я знал о sockaddr_storage, прежде чем я начал этот класс, я бы использовал это вместо объединения. В любом случае, это позволяет мне написать код следующим образом:

CSocketAddress addr;
...
sendto(sock, buffer, len, 0, addr.GetSockAddr(), addr.GetSockAddrLength());

Аналогично, выражение "accept" выглядит следующим образом:

sockaddr_storage addrstorage = {};
int len = sizeof(sockaddr_storage);
accept(sock, (sockaddr*)&addrstorage, &len);

CSocketAdddress addr(addrstorage); // construct an address object to pass around everywhere else

Это было невероятно полезно для путей кода, которые вызывают bind, send и recv. Теперь мои пути к серверу STUN и клиентским кодам больше не должны знать ничего о семейном типе адреса сокета. Они просто работают с объектами "CSocketAddress". Единственный специфичный для IPV4 и IPV6 код - во время инициализации клиента и сервера - когда объекты адреса фактически построены. К счастью, это было частично абстрагировано.

Вы также можете ознакомиться с вспомогательными функциями здесь. Там есть еще один полезный материал для решения имен хостов, перечисления адаптеров и т.д. В агностическом смысле IP. Это код Linux, но некоторые из них должны соответствовать окну Windows и winsock.

Я почти сделал добавление поддержки TCP к этой базе кода. В процессе добавления поддержки для SOCK_STREAM мне не пришлось делать ни одного изменения, ни добавлять новый код для устранения различий в структурах адресов IPV4 и IPV6.

Ответ 2

Обычно я не вижу необходимости struct sockaddr_storage. Цель состоит в том, чтобы выделить достаточное пространство для структуры sockaddr любого заданного протокола, но как часто вам нужно это делать в коде агностической версии IP-версии? Как правило, вы вызываете getaddrinfo(), и он дает вам кучу struct sockaddr * s, и вам все равно, являются ли они sockaddr_in или sockaddr_in6, вы просто передаете их как-есть на bind() и connect() (не требуется литье).

В типичном коде клиента/сервера основным местом, где я могу думать о том, где struct sockaddr_storage полезно, является резервирование места для второго параметра до accept(). В этом случае я согласен с тем, что нужно убрать его на struct sockaddr * один раз за accept() и снова за getnameinfo(). Но я не вижу пути вокруг этих отливок. Это C. Наследование структуры всегда включает в себя множество отливок.