API, использующий sockaddr_storage - PullRequest
20 голосов
/ 12 января 2012

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

И приведение к sockaddr_in и sockaddr_in6 по отдельности побеждает цель моего попытки использовать sockaddr_storage.

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

Ответы [ 2 ]

25 голосов
/ 13 января 2012

Проблема совместного выполнения программ 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());

Аналогично, выражение «принять» выглядит так:

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.

3 голосов
/ 12 января 2012

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

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

...