2015-09-17 34 views
5
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 

La struttura effettiva passata per l'argomento addr dipenderà dalla famiglia di indirizzi. La struttura sockaddr è definito come qualcosa di simile a:Qual è lo scopo del campo sa_data in un sockaddr?

struct sockaddr { 
    sa_family_t sa_family; 
    char  sa_data[14]; 
} 

Così, per un indirizzo IPv4 (AF_INET), la struct effettivo che verrà passato è questo:

/* Source http://linux.die.net/man/7/ip */ 

struct sockaddr_in { 
    sa_family_t sin_family; /* address family: AF_INET */ 
    in_port_t  sin_port; /* port in network byte order */ 
    struct in_addr sin_addr; /* internet address */ 
}; 

/* Internet address. */ 
struct in_addr { 
    uint32_t  s_addr;  /* address in network byte order */ 
}; 

funziona il codice di bind leggere il valore sockaddr.sa_family e in base al valore rilevato, verrà quindi lanciata la struttura sockaddr nella struttura appropriata, ad esempio sockaddr_in?

Perché lo sa_data è impostato su 14 caratteri? Se ho capito bene, il campo sa_data è solo un campo che avrà uno spazio di memoria sufficiente per adattarsi a tutti i tipi di famiglie di indirizzi? Presumibilmente i designer originali hanno previsto che 14 personaggi sarebbero stati abbastanza ampi da adattarsi a tutti i tipi futuri.

+0

https://en.wikipedia.org/wiki/Type_punning – user3386109

risposta

5

Secondo il glibc manual:

La lunghezza 14 del sa_data è essenzialmente arbitraria.

E il FreeBSD developers handbook menzioni il seguente:

Si prega di notare la vaghezza con cui viene dichiarato il campo sa_data, proprio come un array di 14 byte, con il commento suggerendo ci possono essere più di 14 di loro.

Questa vaghezza è piuttosto deliberata. Socket è un'interfaccia molto potente . Mentre la maggior parte delle persone forse la pensa come nient'altro che l'interfaccia Internet e la maggior parte delle applicazioni probabilmente la usano per quello al giorno d'oggi i socket possono essere usati per qualsiasi tipo di comunicazione interprocess , di cui Internet (o, più precisamente , IP) è solo uno.

Sì, il campo sa_family viene utilizzato per riconoscere come trattare lo struct passato (che è il cast a struct sockaddr* in una chiamata a legare). Puoi leggere ulteriori informazioni su come funziona anche in un FreeBSD developers handbook.

E in realtà ci sono "polimorfica" (sotto) tipi di sockaddr, in cui sa_data contiene più di 16 byte, ad esempio:

struct sockaddr_un { 
    sa_family_t sun_family;    /* AF_UNIX */ 
    char  sun_path[108];   /* pathname */ 
}; 
3

La sockaddr struct viene utilizzato come unione etichettato. Leggendo il campo sa_family è possibile eseguire il cast su una struttura del modulo corretto.

I 14 byte sono arbitrari. È abbastanza grande da contenere indirizzi IPv4, ma non abbastanza grande da contenere indirizzi IPv6. C'è anche una struttura sockaddr_storage che è abbastanza grande per entrambi. Leggendo i documenti Microsoft su SOCKADDR_STORAGE, è disponibile a 128 byte, quindi molto più grande di quanto necessario per IPv6. Controllando alcune intestazioni di Linux, sembra che ci sia almeno altrettanto grande.

Per riferimento, lo struct IPv6 è:

struct sockaddr_in6 { 
    u_int16_t  sin6_family; // address family, AF_INET6 
    u_int16_t  sin6_port;  // port number, Network Byte Order 
    u_int32_t  sin6_flowinfo; // IPv6 flow information 
    struct in6_addr sin6_addr;  // IPv6 address 
    u_int32_t  sin6_scope_id; // Scope ID 
}; 

struct in6_addr { 
    unsigned char s6_addr[16]; // IPv6 address 
}; 

Come si può vedere, il 16 byte di s6_addr campo è già più grande di 14 byte sa_data campo su di essa la propria. La dimensione totale dopo il campo sa_family è di 26 byte.