IPv4套接字地址結構通常也稱為“網際套接字地址結構”,它以sockaddr_in命名,定義在<netinet/in.h>頭文件中。
struct in_addr
{
in_addr_t s_addr;/*32-bit IPv4 address*/
/*network byte ordered*/
};
struct sockaddr_in
{
uint8_t sin_len;/*length if structure(16)*/
sa_family_t sin_family;/*AF_INET*/
in_port_t sin_port;/*16-bit TCP or UDP port number*/
/*network byte ordered*/
struct in_addr sin_addr;/*32-bit IPv4 address*/
/*network byte ordered*/
char sin_zero[8]; /*unused*/
};
對套接字地址結構做幾點一般性的說明。
1.長度字段sin_len是為了增加對OSI協議的支持而隨4.3BSD-Reno添加的。在此之前,第一個成員是sin_family,它是一個無符號短整數(unsigned short)。並不是所有的廠家都支持套接字地址結構的長度字段,而且POSIX規范也不要求有這個成員。該成員的數據類型uint8_t是典型的,符合POSIX的系統都提供這種形式的 數據類型。
正是因為有了長度字段,才簡化了長度可變套接字地址結構的處理。
2.即使有長度字段,我們也無須設置和檢查它,除非設計路由套接字。它是由處理來自不同協議族的套接字地址結構例程在內核中使用的。
3.POSIX規范只需要這個結構中的3個字段:sin_family、sin_addr和sin_port。對於符合POSIX的實現來說,定義額外的結構字段是可以接受的,這對於網際套接字地址結構來說也是正常的。幾乎所有的實現都增加了sin_zero字段,所以所有的套接字地址結構大小都至少是16字節。
4.我們個出字段s_addr、sin_family和sin_port的POSIX數據結構類型。in_addr_t數據類型必須是一個至少32位的無符號整數類型,in_port_t必須是一個至少16位的無符號整數類型,而sa_family_t可以是任何無符號整數類型。在支持長度字段的實現中,sa_family_t通常是一個8位的無符號整數,而在不支持長度字段的實現中,它則是一個16位的無符號整數。

5.我們還將遇到數據類型u_char、u_short、u_int和u_long,它們都是無符號的。POSIX規范定義這些類型時特地標記它們已過時,僅是為向后兼容才提供的。
6.IPv4地址和TCP或UDP端口號在套接字地址結構中總是以網絡字節序來存儲。在使用這些字段時,我們必須牢記這一點。
7.32位IPv4地址存在兩種不用的訪問方法。舉例來說,如果serv定義為某個網際套接字地址結構,那么serv.sin_addr將按in_addr結構引用其中的32位IPv4地址,而serv.sin_addr.s_addr將按in_addr_t(通常是一個無符號的32位整數)引用同一個32位IPv4地址。因此,我們必須正確地使用IPv4地址,尤其是在將它作為函數的參數時,因為編譯器 對傳遞結構和傳遞整數的處理是完全不同的。
sin_addr字段是一個結構,而不僅僅是一個in_addr_t類型的無符號長整數,這是有歷史原因的。早期的版本(4.2BSD)把in_adddr結構定義為多種結構的聯合(union),允許訪問一個32位IPv4地址中的所有4個字節,或者訪問它的2個16位值。這用在地址被划分成A、B和C三類的時期,便於獲取地址中的適當字節。然而隨着子網划分技術的來臨和無類地址編排的出現,各種地址類正在消失,那個聯合已不再需要了。如今大多數系統已經廢除了該聯合,轉而把in_addr定義為僅有一個in_addr_t字段的結構。
8.sin_zero字段未曾使用,不過在填寫這種套接字地址結構時,我們總是把該字段置為0.按照慣例,我們總是在填寫前把整個結構置為0,而不是單單把sin_zero字段置為0.
盡管多數使用該結構的情況不要求這一字段為0,但是當捆綁一個非通配的IPv4地址時,該字段必須為0
9.套接字地址結構僅在給定主機上使用:雖然結構中的某些字段(例如IP地址和端口號)用在不同主機之間的通信中,但是結構本身不再主機之間傳遞。
通用套接字地址結構
當作為一個參數傳遞進任何套接字函數時,套接字地址結構總是以引用形式(也就是以指向該結構的指針)來傳遞。然而以這樣的指針作為參數之一的任何套接字函數必須處理來自所支持的任何協議族的套接字地址結構。
在如何聲明所傳遞指針的數據類型上存在一個問題。有了ANSIC后解決辦法很簡單:void是通用的指針類型。然而套接字函數是在ANSIC之前定義的,在1982年采取的辦法是在<sys/socket.h>頭文件中定義一個通用的套接字地址結構,
struct sockaddr
{
uint8_t sa_len;
sa_family_t sa_family;/*address family:AF_xxx value*/
char sa_data[14];/*protocol-specific address*/
}
於是套接字函數被定義為以指向某個通用套接字地址結構的一個指針作為其參數之一,這正如bind函數的ANSIC函數原型所示:
int bind(int,struct sockaddr*,socklen_t);
這就要求對這些函數的任何調用都必須要將指向特定於協議的套接字地址結構的指針進行類型強制轉換(casting),變成指向某個通用套接字地址結構的指針,例如:
struct sockaddr_in serv /*IPv4 socket address strcuture*/
/*fill inserv{}*/
bind (sockfd,(struct sockaddr*)&serv,sizeof(serv));
如果我們省略了其中的類型強制轉換部分“(struct sockaddr*)”,並假設系統的頭文件中有bind函數的ANSIC原型,那么 編譯器就會產生這樣的警告信息:“warning:passsing arg 2 of bind from incompatible pointer type.”(警告:把不兼容的指針類型傳遞給“bind”函數的第二個參數。)
從應用程序開發人員的觀點看,這些通用套接字地址結構的唯一用途就是對特定於協議的套接字地址結構的指針執行類型強制轉換。
IPv6套接字地址結構
IPv6套接字地址結構在<netinet/in.h>頭文件中定義,
struct in6_addr
{
unit8_t s6_addr[16];/*128-bit IPv6 address*/
/*network byte ordered*/
};
#define SIN6_LEN/*required for compile-time tests*/
struct sockaddr_in6
{
uint8_t sin6_len;/*length of this struct(28)*/
sa_family_t sin_family;/*AF_INET6*/
in_port_t sin6_port;/*transport layer port*/
/*network byte ordered*/
uint32_t sin6_flowinfo;/*flow information,undefined*/
struct in6_addr sin6_addr;/*IPv6 address*/
/*network byte ordered*/
uint32_t sin6_scope_id /*set of interfaces for a scope*/
};
1.如果系統支持套接字地址結構中的長度字段,那么SIN6_LEN常值必須定義。
2.IPv6的地址族是AF_INET6,而IPv4的地址族是AF_INET。
3.結構中字段的先后順序做過編排,使得如果是sockaddr_in6結構本身是64位對齊的那么128的sin6_addr字段也是64位對齊的。在一些64位處理機上,如果64 位數據存儲在某個64位邊界位置,那么對它的訪問將得到優化處理。
4.sin6_flowinfo字段分成兩個字段:
低序20位是流標(flow label);
高序12位保留。
5.對於具備范圍的地址(scoped address),sin6_scope_id字段標識其范圍(scope),最常見的是鏈路局部地址(link-local-address)的套接索引(interface index)
新的通用套接字地址結構
作為IPv6套接字API的一部分而定義的新的通用套接字地址結構克服了現又struct sockaddr 的一些缺點。不像struct sokcaddr,新的struct sockaddr_shortage足以容納系統所支持的任何套接字地址結構。sockaddr_shorage結構在<netinet/in.h>頭文件中定義
struct sockaddr_shorage
{
uint8_t ss_len;/*length of this struct(implementation dependent)*/
sa_family_t ss_family;/*address family:AF_xxx value*/
/*implementation-dependent elements to provide:
a)alignment sufficient to fulfill the alignment requirements of
all socket address types that sysytem supports
b)enough storage to hold any type of socket address that the
system supports*/
};
sockaddr_shorage類型提供的通用套接字地址結構相比sockaddr存在以下兩點差別。
(1)如果系統支持的任何套接字地址結構有對齊需要,那么sockaddr_shorage能夠滿足最苛刻的對齊要求。
(2)sockaddr_shorage足夠大,能夠容納系統支持的任何套接字地址結構。
注意,除了ss_family和ss_len外(如果有的話),sockaddr_shorage結構中的其他字段對用戶來說是透明。sockaddr_shorage結構必須類型強制轉換成或復制到合適於ss_family 字段所給出地址類型的套接字地址結構中,才能夠訪問其他字段。
套接字地址結構的比較

在上圖中,我們對5種套接字地址結構進行了比較IPv4、IPv6、Unix域、數據鏈路和存儲。在該圖中,我們假設所有套接字地址結構都包含一個單字節的長度字段,地址族字段也占一個字節,其他所有字段都占用確切的最短長度。
前面兩種套接字地址結構是固定長度的,而 Unix域結構和數據鏈路結構是可變長度的。為了處理長度可變的結構,當我們把指向某個套接字地址結構的指針作為一個參數傳遞給某個套接字函數時,也把該結構的長度作為另一個參數傳遞給這個函數。我們在每種長度固定的結構下方給出了這種結構的字節長度
sockaddr_un結構本身並非長度可變的,但是其中的信息(即結構中的路徑名)卻是長度可變的。當傳遞指向這些結構的指針時,我們必須小心處理長度字段、包括套接字地址結構本身的長度字段(如果其實現支持此字段),以及作為參數傳給內核或從內核返回的長度。
