GNU/Linux >> Znalost Linux >  >> Linux

Proč při volání bind() přetypujeme sockaddr_in na sockaddr?

Ne, není to jen konvence.

sockaddr je obecný deskriptor pro jakýkoli druh operace soketu, zatímco sockaddr_in je struktura specifická pro komunikaci založenou na IP (IIRC, "in" znamená "InterNet"). Pokud vím, jedná se o určitý druh "polymorfismu":bind() funkce předstírá, že bere struct sockaddr * , ale ve skutečnosti bude předpokládat, že je předán příslušný typ struktury; i. E. ten, který odpovídá typu zásuvky, kterou zadáte jako první argument.


Nevím, jestli je to pro tuto otázku příliš relevantní, ale rád bych poskytl nějaké další informace, díky nimž bude typová kasta srozumitelnější pro mnoho lidí, kteří s C nestrávili mnoho času být zmatený, když vidíte takovou typickou kastu.

Používám macOS , takže beru příklady založené na hlavičkových souborech z mého systému.

struct sockaddr je definován následovně:

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 je definován následovně:

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];
};

Počínaje úplnými základy, ukazatel obsahuje pouze adresu. Takže struct sockaddr * a struct sockaddr_in * jsou v podstatě stejné. Oba si jen ukládají adresu. Jediný relevantní rozdíl je v tom, jak kompilátor zachází s jejich objekty.

Když tedy řeknete (struct sockaddr *) &name , jen podvádíte kompilátor a říkáte mu, že tato adresa ukazuje na struct sockaddr typ.

Řekněme tedy, že ukazatel ukazuje na umístění 1000 . Pokud je struct sockaddr * uloží tuto adresu, bude uvažovat paměť od 1000 na sizeof(struct sockaddr) mající členy podle definice struktury. Pokud struct sockaddr_in * ukládá stejnou adresu, kterou bude považovat za paměť od 1000 na sizeof(struct sockaddr_in) .

Když napíšete tento ukazatel, bude uvažovat stejnou sekvenci bajtů až do sizeof(struct sockaddr) .

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

Nyní, když přistupuji k a->sa_len , kompilátor přistupuje z umístění 1000 na sizeof(__uint8_t) což je stejná velikost bajtů jako v případě sockaddr_in . Takže by to mělo přistupovat ke stejné sekvenci bajtů.

Stejný vzor je pro sa_family .

Poté je v struct sockaddr pole 14 bajtů znaků který ukládá data z in_port_t sin_port (typedef 'd 16bitové celé číslo bez znaménka =2 bajty ), struct in_addr sin_addr (prostě 32bitová adresa ipv4 =4 bajty) a char sin_zero[8] (8 bajtů). Tyto 3 dohromady tvoří 14 bajtů.

Nyní jsou tyto tři uloženy v tomto 14bajtovém poli znaků a my můžeme přistupovat ke kterémukoli z těchto tří tím, že zpřístupníme příslušné indexy a znovu je přetypujeme.

Odpověď uživatele 529758 již vysvětluje důvod, proč to udělat.


Je to proto, že bind může vázat jiné typy soketů než IP sokety, například unixové doménové sokety, které mají jako typ sockaddr_un. Adresa pro soket AF_INET má jako adresu hostitele a port, zatímco soket AF_UNIX má cestu k souborovému systému.


Linux
  1. Rychlý způsob, jak zahrnout cestu k adresáři při volání Mv?

  2. Kdy a proč bych měl používat Apt-get Update?

  3. Proč se při použití uvozovek zobrazuje jedno zpětné lomítko?

  1. Při použití os.execlp, proč `python` potřebuje `python` jako argv[0]

  2. Proč není `tail -f … | grep -q …` ukončí se, když najde shodu?

  3. Proč LXC, když existuje linux-vserver?

  1. Proč OpenStack hlásí typ Hypervisoru jako QEMU, když libvirt_type je KVM?

  2. Proč je synchronizace tak důležitá při vytváření zaváděcího USB klíče pro Linux?

  3. Proč se používá Swap, když zbývá spousta volné paměti?