Warum wandeln wir sockaddr_in in sockaddr um, wenn wir bind() aufrufen?

Lesezeit: 4 Minuten

Das binden() Die Funktion akzeptiert einen Zeiger auf a sockaddraber in allen Beispielen, die ich gesehen habe, a sockaddr_in stattdessen wird eine Struktur verwendet, in die gecastet wird sockaddr:

struct sockaddr_in name;
...
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
...

Ich kann mir nicht vorstellen, warum a ist sockaddr_in Struktur verwendet. Warum nicht einfach vorbereiten und passieren a sockaddr?

Ist es nur Konvention?

  • NB. sockaddr_in6 existiert auch, und jeder, der neuen Code schreibt, wäre gut beraten, ihn einzufügen …

    – BRPocock

    13. Januar 2014 um 19:00 Uhr

  • Sie haben einen sehr wichtigen Teil in Ihrem Code ausgelassen: name.sa_family = AF_INET zum struct sockaddr_in! In Betracht ziehen struct sockaddr eine Vereinigung aller anderen sockaddr-Typen sein. Die einzige Gemeinsamkeit ist, dass sie ein erstes Mitglied haben sa_family_t sa_family die dem tatsächlichen Strukturtyp entsprechen müssen.

    – Ulrich Eckhardt

    25. Januar 2015 um 10:47 Uhr

Nein, es ist nicht nur Konvention.

sockaddr ist ein generischer Deskriptor für jede Art von Socket-Operation, wohingegen 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, a zu nehmen struct sockaddr *, aber tatsächlich wird davon ausgegangen, dass der entsprechende Strukturtyp übergeben wird; dh eine, die dem Socket-Typ entspricht, den Sie ihm als erstes Argument geben.

  • Nur um hinzuzufügen: sockaddr_in6 ist für IPv6-Adressen, sockaddr_un für Unix-Domain-Sockets, …

    –Martin R

    13. Januar 2014 um 19:00 Uhr

  • @MartinR Ich habe auch über Bluetooth nachgedacht (wenn ich mich nicht irre, macht Linux RFCOMM über Sockets) usw.

    Benutzer529758

    13. Januar 2014 um 19:01 Uhr

  • Es gibt viele Steckdosentypen, die ich nicht kenne … Es wäre vielleicht erwähnenswert struct sockaddr_storagedas in gewissem Sinne auch “generisch” ist und groß genug ist, um jede Art von Socket-Adresse aufzunehmen.

    –Martin R

    13. Januar 2014 um 19:06 Uhr

Benutzeravatar von Mihir Luthra
Michir Luthra

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 damit verbracht haben C verwirrt werden, wenn man eine solche Typisierung sieht.

ich benutze macOSalso 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. So 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.

Also wenn du sagst (struct sockaddr *) &nametäuschen Sie nur den Compiler und sagen ihm, dass diese Adresse auf a zeigt struct sockaddr Typ.


Nehmen wir also an, der Zeiger zeigt auf einen Ort 1000. Wenn die struct sockaddr * speichert diese Adresse, wird es Speicher aus berücksichtigen 1000 zu sizeof(struct sockaddr) Besitz der Mitglieder gemäß der Strukturdefinition. Wenn struct sockaddr_in * speichert die gleiche Adresse, von der es den Speicher berücksichtigt 1000 zu sizeof(struct sockaddr_in).


Wenn Sie diesen Zeiger typisiert haben, wird dieselbe Folge von Bytes bis zu berücksichtigt sizeof(struct sockaddr).

struct sockaddr *a = &name; // consider &name = 1000

Wenn ich jetzt zugreife a->sa_lenwürde der Compiler von Ort aus zugreifen 1000 zu sizeof(__uint8_t) das ist die gleiche Bytegröße wie im Fall von sockaddr_in. Dies sollte also auf dieselbe Folge von Bytes zugreifen.

Das gleiche Muster ist für sa_family.

Danach gibt es ein 14-Byte-Zeichen-Array struct sockaddr die Daten von speichert in_port_t sin_port (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 Byte). Diese 3 ergeben zusammen 14 Bytes.

Jetzt werden diese drei in diesem 14-Byte-Zeichenarray gespeichert, und wir können auf alle diese drei zugreifen, indem wir auf die entsprechenden Indizes zugreifen und sie erneut typisieren.

Die Antwort von user529758 erklärt bereits den Grund dafür.

Benutzeravatar von Magnus Reftel
Magnus Reftel

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.

1414510cookie-checkWarum wandeln wir sockaddr_in in sockaddr um, wenn wir bind() aufrufen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy