No, non è solo convenzione.
sockaddr
è un descrittore generico per qualsiasi tipo di operazione socket, mentre sockaddr_in
è una struttura specifica per la comunicazione basata su IP (IIRC, "in" sta per "InterNet"). Per quanto ne so, questo è un tipo di "polimorfismo":il bind()
la funzione finge di prendere un struct sockaddr *
, ma in realtà presupporrà che sia passato il tipo di struttura appropriato; io. e. uno che corrisponde al tipo di socket che gli dai come primo argomento.
Non so se sia molto rilevante per questa domanda, ma vorrei fornire alcune informazioni extra che potrebbero rendere il typecaste più comprensibile dato che molte persone che non hanno trascorso molto tempo con C
confondersi vedendo una tale casta.
Uso macOS
, quindi sto prendendo esempi basati su file di intestazione dal mio sistema.
struct sockaddr
è definito come segue:
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
è definito come segue:
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];
};
Partendo dalle basi, un puntatore contiene solo un indirizzo. Quindi struct sockaddr *
e struct sockaddr_in *
sono praticamente la stessa cosa. Entrambi memorizzano solo un indirizzo. L'unica differenza rilevante è il modo in cui il compilatore tratta i propri oggetti.
Quindi quando dici (struct sockaddr *) &name
, stai solo ingannando il compilatore e dicendogli che questo indirizzo punta a un struct sockaddr
digitare.
Quindi diciamo che il puntatore punta a una posizione 1000
. Se struct sockaddr *
memorizza questo indirizzo, considererà la memoria da 1000
a sizeof(struct sockaddr)
in possesso dei membri secondo la definizione della struttura. Se struct sockaddr_in *
memorizza lo stesso indirizzo che considererà memoria da 1000
a sizeof(struct sockaddr_in)
.
Quando hai digitato quel puntatore, considererà la stessa sequenza di byte fino a sizeof(struct sockaddr)
.
struct sockaddr *a = &name; // consider &name = 1000
Ora se accedo a a->sa_len
, il compilatore accederà dalla posizione 1000
a sizeof(__uint8_t)
che è la stessa dimensione in byte del caso di sockaddr_in
. Quindi questo dovrebbe accedere alla stessa sequenza di byte.
Lo stesso modello è per sa_family
.
Dopodiché c'è un array di caratteri di 14 byte in struct sockaddr
che memorizza i dati da in_port_t sin_port
(typedef
'd Intero senza segno a 16 bit =2 byte ) , struct in_addr sin_addr
(semplicemente un indirizzo ipv4 a 32 bit =4 byte) e char sin_zero[8]
(8 byte). Questi 3 sommati danno 14 byte.
Ora questi tre sono memorizzati in questo array di caratteri da 14 byte e possiamo accedere a uno qualsiasi di questi tre accedendo agli indici appropriati e digitandoli di nuovo.
la risposta di user529758 spiega già il motivo per farlo.
Questo perché bind può associare altri tipi di socket rispetto ai socket IP, ad esempio socket di dominio Unix, che hanno sockaddr_un come tipo. L'indirizzo per un socket AF_INET ha l'host e la porta come indirizzo, mentre un socket AF_UNIX ha un percorso del filesystem.