Non, ce n'est pas seulement une convention.
sockaddr
est un descripteur générique pour tout type d'opération de socket, alors que sockaddr_in
est une structure spécifique à la communication basée sur IP (IIRC, "in" signifie "Internet"). A ma connaissance, c'est une sorte de "polymorphisme" :le bind()
la fonction fait semblant de prendre un struct sockaddr *
, mais en fait, il supposera que le type de structure approprié est transmis ; je. e. celui qui correspond au type de socket que vous lui donnez comme premier argument.
Je ne sais pas si c'est très pertinent pour cette question, mais je voudrais fournir quelques informations supplémentaires qui peuvent rendre la typographie plus compréhensible car beaucoup de gens qui n'ont pas passé beaucoup de temps avec C
devenir confus en voyant une telle typographie.
J'utilise macOS
, donc je prends des exemples basés sur les fichiers d'en-tête de mon système.
struct sockaddr
est défini comme suit :
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
struct sockaddr_in
est défini comme suit :
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
À partir des bases, un pointeur contient juste une adresse. Alors struct sockaddr *
et struct sockaddr_in *
sont à peu près les mêmes. Ils stockent tous les deux une adresse. La seule différence pertinente est la façon dont le compilateur traite ses objets.
Donc, quand vous dites (struct sockaddr *) &name
, vous trompez simplement le compilateur et lui dites que cette adresse pointe vers un struct sockaddr
saisir.
Supposons donc que le pointeur pointe vers un emplacement 1000
. Si le struct sockaddr *
stocke cette adresse, il considérera la mémoire de 1000
à sizeof(struct sockaddr)
possédant les membres selon la définition de la structure. Si struct sockaddr_in *
stocke la même adresse, il considérera la mémoire de 1000
à sizeof(struct sockaddr_in)
.
Lorsque vous avez transtypé ce pointeur, il considérera la même séquence d'octets jusqu'à sizeof(struct sockaddr)
.
struct sockaddr *a = &name; // consider &name = 1000
Maintenant, si j'accède à a->sa_len
, le compilateur accéderait à partir de l'emplacement 1000
à sizeof(__uint8_t)
qui est la même taille en octets que dans le cas de sockaddr_in
. Cela devrait donc accéder à la même séquence d'octets.
Le même modèle est pour sa_family
.
Après cela, il y a un tableau de caractères de 14 octets dans struct sockaddr
qui stocke les données de in_port_t sin_port
(typedef
'd entier non signé 16 bits =2 octets ) , struct in_addr sin_addr
(simplement une adresse ipv4 32 bits =4 octets) et char sin_zero[8]
(8 octets). Ces 3 s'additionnent pour faire 14 octets.
Maintenant, ces trois sont stockés dans ce tableau de caractères de 14 octets et nous pouvons accéder à l'un de ces trois en accédant aux index appropriés et en les transtypant à nouveau.
La réponse de user529758 explique déjà la raison de le faire.
En effet, bind peut lier d'autres types de sockets que les sockets IP, par exemple les sockets de domaine Unix, qui ont sockaddr_un comme type. L'adresse d'un socket AF_INET a l'hôte et le port comme adresse, tandis qu'un socket AF_UNIX a un chemin de système de fichiers.