套接字地址結構

sturdygrass發表於2013-05-21

大多數套接字函式都需要一個指向套接字地址結構的指標作為引數。每個協議族都定義了它自己的套接字地址結構。這些結構的名字均以sockaddr_開頭,並以對應每個協議族的唯一字尾結尾。

1IPv4套接字地址結構

IPv4套接字地址結構通常也稱為“網際套接字地址結構”,它以sockaddr_in命名,定義在<netinet/in.h>標頭檔案中。圖1給出了它的POSIX定義:

  struct in_addr {
  in_addr_t		s_addr;			/* 32-bit IPv4 address */
  /* network byte ordered */
  };
  
  struct sockaddr_in {
  uint8_t		sin_len;			/* length of 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 */
  };

網際(IPv4)套接字地址結構:sockaddr_in

 

利用圖1所示的例子,我們對套接字地址結構做幾點一般性的說明:

•長度欄位sin_len是為增加對OSI協議的支援而隨4.3BSD-Reno新增的。在此之前,第一個成員是sin_family,它是一個無符號短整數(unsigned short)。並不是所有的廠家都支援套接字地址結構的長度欄位,而且POSIX規範也不要求有這個成員。

•即使有長度欄位,我們也無須設定和檢查它,除非涉及路由套接字。它是由處理來自不同協議族的套接字地址結構的例程(例如路由表處理程式碼)在核心中使用的。

•POSIX規範只需要這個結構中的3個欄位:sin_familysin_addrsin_port。對於符合POSIX的實現來說,定義額外的結構欄位是可以接受的,這對於網際套接字地址結構來說也是正常的。幾乎所有的實現都增加了sin_zero欄位,所以所有的套接字地址結構大小都至少是16位元組。

•我們給出了欄位s_addrsin_familysin_portPOSIX資料型別。in_addr_t資料型別必須是一個至少32位的無符號整數型別,in_port_t必須是一個至少16位的無符號整數型別,而sa_family_t可以是任何無符號整數型別。在支援長度欄位的實現中,sa_family_t通常是一個8位的無符號整數,而在不支援長度欄位的實現中,它則是一個16位的無符號整數。圖2列出了POSIX定義的這些資料型別。

2 POSIX規範要求的資料型別

IPv4地址和TCPUDP埠號在套接字地址結構中總是以網路位元組序來儲存。

sin_addr欄位是一個結構,而不僅僅是一個in_addr_t型別的無符號長整數,這是有歷史原因的。早期的版本(4.2BSD)把in_addr結構定義為多種結構的聯合(union),允許訪問一個32IPv4地址中的所有4個位元組,或者訪問它的216位值。這用在地址被劃分成ABC三類的時期,便於獲取地址中的適當位元組。然而隨著子網劃分技術的來臨和無類地址編排的出現,各種地址類正在消失,那個聯合已不再需要了。如今大多數系統已經廢除了該聯合,轉而把in_addr定義為僅有一個in_addr_t欄位的結構。

sin_zero欄位未曾使用,不過在填寫這種套接字地址結構時,我們總是把該欄位置為0。按照慣例,我們總是在填寫前把整個結構置為0,而不是單單把sin_zero欄位置為0。儘管多數使用該結構的情況不要求這一欄位為0,但是當捆綁一個非通配的IPv4地址時,該欄位必須為0

2、通用套接字地址結構

當作為一個引數傳遞進任何套接字函式時,套接字地址結構總是以引用形式(也就是以指向該結構的指標)來傳遞。然而以這樣的指標作為引數之一的任何套接字函式必須處理來自所支援的任何協議族的套接字地址結構。

在如何宣告所傳遞指標的資料型別上存在一個問題。有了ANSI C後解決辦法很簡單:void*是通用的指標型別。然而套接字函式是在ANSI C之前定義的,在1982年採取的辦法是在<sys/socket.h>標頭檔案中定義一個通用的套接字地址結構,如圖3所示:

  struct sockaddr {
  uint8_t		sa_len;
  sa_family_t	sa_family;		/* address family: AF_XXX value */
  char			sa_data[14];		/* protocol-specific address */
  };

通用套接字地址結構:sockaddr

於是套接字函式被定義為以指向某個通用套接字地址結構的一個指標作為其引數之一,這正如bind函式的ANSI C函式原型所示:

int bind(int, struct sockaddr *, socklen_t);

3、值-結果引數

1)從程式到核心傳遞套接字地址結構的函式有3個:bindconnectsendto。這些函式的一個引數是指向某個套接字地址結構的指標,另一個引數是該結構的整數大小,例如:

  struct sockaddr_in serv;
  
  /* fill in serv{} */
  connect(sockfd, (struct sockaddr*)&serv, sizeof(serv));

2)從核心到程式傳遞套接字地址結構的函式有4個:acceptrecvfromgetsocknamegetpeername。這4個函式的其中兩個引數是指向某個套接字地址結構的指標和指向表示該結構大小的整數變數的指標。例如:

  struct sockaddr_in	cli;
  socklen_t			len;
  
  len = sizeof(cli);
  getpeername(sockfd, (struct sockaddr*)&cli, &len);
  /* len may have changed */

把套接字地址結構大小這個引數從一個整數改為指向某個整數變數的指標,其原因在於:當函式被呼叫時,結構大小是一個值,它告訴核心該結構的大小,這樣核心在寫該結構時不至於越界;但函式返回時,結構大小又是一個結果,它告訴程式核心在該結構中究竟儲存了多少資訊。這種型別的引數稱為值-結果引數。

 

相關文章