web-dev-qa-db-fra.com

Quelle est la différence entre sockaddr, sockaddr_in et sockaddr_in6?

Je sais que sockaddr_in est pour IPv4 et sockaddr_in6 pour IPv6. La confusion pour moi est la différence entre sockaddr et sockaddr_in [6].

Certaines fonctions acceptent sockaddr et certaines fonctions acceptent sockaddr_in Ou sockaddr_in6, Donc:

  • quelle est la règle?
  • Et pourquoi faut-il deux structures différentes?

Et parce que la sizeof(sockaddr_in6) > sizeof(sockaddr) == sizeof(sockaddr_in).

  • Cela signifie-t-il que nous devrions toujours utiliser sockaddr_in6 pour allouer de la mémoire dans la pile et convertir en sockaddr et sockaddr_in si nous devons prendre en charge ipv4 et ipv6?

Un exemple est: nous avons un socket, et nous voulons en obtenir l'adresse IP de chaîne (elle peut être ipv4 ou ipv6).

Nous appelons d'abord getsockname pour obtenir un addr puis appelons inet_ntop Sur la base de addr.sa_family.

Y a-t-il un problème avec cet extrait de code?

char ipStr[256];
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);

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));
}
41
ZijingWu

Je ne veux pas répondre à ma question. Mais pour donner plus d'informations ici qui pourraient être utiles à d'autres personnes, je décide de répondre à ma question.

Après Dig dans le code source de linux. Voici ma conclusion, il existe plusieurs protocoles possibles qui implémentent tous le getsockname. Et chacun a lui-même une structure de données d'adresse sous-jacente, par exemple, pour IPv4 c'est sockaddr_in, Et IPV6 sockaddr_in6 Et sockaddr_un Pour la prise AF_UNIX. sockaddr sont utilisés comme support de données commun dans la signature de ces API.

Ces API vont copier socketaddr_in ou sockaddr_in6 ou sockaddr_un sur sockaddr sur la base d'un autre paramètre length par memcpy.

Et toute la structure de données commence par le même champ de type sa_family.

Sur la base de cette raison, l'extrait de code est valide, car sockaddr_in Et sockaddr_in6 Ont sa_family Et nous pouvons ensuite le convertir dans la structure de données appropriée pour une utilisation après vérification sa_family.

BTY, je ne sais pas pourquoi la sizeof(sockaddr_in6) > sizeof(sockaddr), qui cause l'allocation de la base de mémoire sur la taille de sockaddr n'est pas suffisante pour ipv6 (qui est sujette aux erreurs), mais je suppose que c'est pour une raison historique.

20
ZijingWu

sockaddr_in Et sockaddr_in6 Sont les deux structures où le premier membre est une structure sockaddr.

Selon la norme C, l'adresse d'une structure et son premier membre sont les mêmes, vous pouvez donc convertir le pointeur en sockaddr_in(6) en un pointeur en sockaddr.

Les fonctions prenant sockaddr_in(6) comme paramètre peuvent modifier la partie sockaddr, et les fonctions prenant sockaddr comme paramètre se soucient juste de cette partie.

C'est un peu comme l'héritage.

25
linkdd