Nein, es ist nicht nur eine Konvention.
sockaddr
ist ein generischer Deskriptor für jede Art von Socket-Operation, während sockaddr_in
ist eine spezifische Struktur für die IP-basierte Kommunikation (IIRC, „in“ steht für „Internet“). Soweit ich weiß, ist dies eine Art "Polymorphismus":die bind()
Funktion gibt vor, eine struct sockaddr *
zu nehmen , aber tatsächlich wird davon ausgegangen, dass der entsprechende Strukturtyp übergeben wird; ich. e. eine, die dem Socket-Typ entspricht, den Sie als erstes Argument angeben.
Ich weiß nicht, ob es für diese Frage sehr relevant ist, aber ich möchte einige zusätzliche Informationen bereitstellen, die die Typisierung für viele Menschen verständlicher machen, die nicht viel Zeit mit C
verbracht haben verwirrt werden, wenn man so eine Typisierung sieht.
Ich verwende macOS
, also nehme ich Beispiele basierend auf Header-Dateien von meinem System.
struct sockaddr
ist wie folgt definiert:
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
ist wie folgt definiert:
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];
};
Ausgehend von den Grundlagen enthält ein Zeiger nur eine Adresse. Also struct sockaddr *
und struct sockaddr_in *
sind ziemlich gleich. Beide speichern nur eine Adresse. Der einzige relevante Unterschied besteht darin, wie der Compiler seine Objekte behandelt.
Wenn Sie also (struct sockaddr *) &name
sagen , täuschen Sie nur den Compiler und sagen ihm, dass diese Adresse auf eine struct sockaddr
zeigt Typ.
Nehmen wir also an, der Zeiger zeigt auf einen Ort 1000
. Wenn die struct sockaddr *
diese Adresse speichert, wird der Speicher von 1000
berücksichtigt bis sizeof(struct sockaddr)
Besitz der Mitglieder gemäß der Strukturdefinition. Wenn struct sockaddr_in *
speichert die gleiche Adresse, die es als Speicher von 1000
betrachtet bis sizeof(struct sockaddr_in)
.
Wenn Sie diesen Zeiger typisiert haben, wird dieselbe Folge von Bytes bis sizeof(struct sockaddr)
berücksichtigt .
struct sockaddr *a = &name; // consider &name = 1000
Wenn ich jetzt auf a->sa_len
zugreife , würde der Compiler von Speicherort 1000
zugreifen bis sizeof(__uint8_t)
das ist dieselbe Bytegröße wie im Fall von sockaddr_in
. Dies sollte also auf dieselbe Folge von Bytes zugreifen.
Dasselbe Muster gilt für sa_family
.
Danach gibt es in struct sockaddr
ein 14-Byte-Zeichenarray die Daten von in_port_t sin_port
speichert (typedef
'd 16-Bit-Ganzzahl ohne Vorzeichen =2 Bytes ) , struct in_addr sin_addr
(einfach eine 32-Bit-IPv4-Adresse =4 Bytes) und char sin_zero[8]
(8 Bytes). Diese 3 ergeben zusammen 14 Bytes.
Jetzt werden diese drei in diesem 14-Byte-Zeichenarray gespeichert, und wir können auf alle drei zugreifen, indem wir auf die entsprechenden Indizes zugreifen und sie erneut typisieren.
Die Antwort von user529758 erklärt bereits den Grund dafür.
Dies liegt daran, dass bind andere Arten von Sockets als IP-Sockets binden kann, zum Beispiel Unix-Domain-Sockets, die sockaddr_un als Typ haben. Die Adresse für einen AF_INET-Socket hat den Host und den Port als Adresse, während ein AF_UNIX-Socket einen Dateisystempfad hat.