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