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

Какая разница между sockaddr, sockaddr_in и sockaddr_in6?

Я знаю, что sockaddr_in для IPv4 и sockaddr_in6 для IPv6. Меня замешала разница между sockaddr и sockaddr_in [6].

Некоторые функции принимают sockaddr, а некоторые функции принимают sockaddr_in или sockaddr_in6, поэтому:

  • какое правило?
  • И почему существует потребность в двух разных структурах?

И поскольку sizeof(sockaddr_in6) > sizeof(sockaddr) == sizeof(sockaddr_in).

  • Означает ли это, что мы всегда должны использовать sockaddr_in6 для выделения памяти в стеке и приведения к sockaddr и sockaddr_in, если нам нужно поддерживать ipv4 и ipv6?

Один пример: у нас есть сокет, и мы хотим получить его IP-адрес (это может быть ipv4 или ipv6).

Сначала вызываем getsockname для получения addr, а затем вызываем inet_ntop на основе addr.sa_family.

Что-то не так с этим фрагментом кода?

sockaddr_in6 addr_inv6;
sockaddr* addr = (sockaddr*)&addr_inv6;
sockaddr_in* addr_in = (sockaddr_in*)&addr_inv6;

socklen_t len = sizeof(addr_inv6);
getsockname(_socket, addr, &len);
char ipStr[256];
if (addr->sa_family == AF_INET6)
{
    inet_ntop(addr_inv6.sin6_family, &addr_inv6.sin6_addr, ipStr, sizeof(ipStr)); 
    // <<<<<<<<IS THIS LINE VALID, getsockname expected a sockaddr, but we use it output parameter as sockaddr_in6.
}
else
{
    inet_ntop(addr_in->sin_family, &addr_in->sin_addr, ipStr, sizeof(ipStr));
}
4b9b3361

Ответ 1

Я не хочу отвечать на мой вопрос. Но чтобы предоставить больше информации, которая может быть полезной для других людей, я решаю ответить на мой вопрос.

Покопайтесь в исходный код linux. Следующее - это мой вывод, есть возможный множественный протокол, который все реализует getsockname. И каждый из них сам подчиняется структуре адресных данных, например, для IPv4 это sockaddr_in, а IPV6 sockaddr_in6 и sockaddr_un для AF_UNIX socket. sockaddr используются в качестве общей стойки данных в сигнатуре этих API.

Эти API будут скопировать файлы socketaddr_in или sockaddr_in6 или sockaddr_un в базу sockaddr по другому параметру length с помощью memcpy.

И вся структура данных начинается с того же поля типа sa_family.

Основываясь на этой причине, фрагмент кода действителен, поскольку sockaddr_in и sockaddr_in6 имеют sa_family, а затем мы можем применить его к правильной структуре данных для использования после проверки sa_family.

BTY, я не уверен, почему sizeof(sockaddr_in6) > sizeof(sockaddr), которые вызывают выделение базы памяти на размер sockaddr, недостаточно для ipv6 (это подвержено ошибкам), но я думаю, что это связано с историей.

Ответ 2

sockaddr_in и sockaddr_in6 - это структуры, в которых первый член представляет собой структуру sockaddr.

В соответствии со стандартом C адрес структуры и ее первый член совпадают, поэтому вы можете направить указатель на sockaddr_in(6) указателем на sockaddr.

Функции, принимающие sockaddr_in(6) в качестве параметра, могут изменять часть sockaddr, а функции принимают sockaddr как параметр, который просто заботится об этой части.

Это немного похоже на наследование.