API с использованием хранилища sockaddr

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

и литье в sockaddr_in и sockaddr_in6 отдельно смысла пытаться использовать sockaddr_storage.

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

2 ответов


проблема совместного программирования 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" может легко начинают повторяться снова и снова. Вот чего ты хочешь избежать.

предполагая, что вы используете C++, вы обнаружите, что класс абстракции для объекта адреса сокета невероятно полезен. У меня есть пример на 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. Теперь мой сервер оглушения и клиентские пути кода больше не должны ничего знать о семейном типе адреса сокета. Они просто работают с объектами "CSocketAddress". Единственный IPv4 и IPV6 специфический код во время инициализация клиента и сервера - когда объекты адреса фактически построены. К счастью, это тоже было частично абстрагировано.

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

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


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

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