二、套接字位址格式
大多數套接字函數都需要一個指向套接字位址結構的指針作為參數。每個協定族都定義了自己的套接字位址結構,這些結構的名字均以sockaddr_開頭,并以對應的每個協定組的唯一字尾結尾
1. IPv4套接字位址結構
IPv4套接字結構通常也稱為“網際套接字位址結構”,它以sockaddr_in命名,定義在netinet/in.h頭檔案中。
struct in_addr{
in_addr_t s_addr;
};
struct sockaddr_in{
unit8_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
strcut in_addr sin_addr; //32-bit IPv4 address netwrok byte order
char sin_zero[];//unused
};
2. 通用套接字結構
當作為一個參數傳遞進任何套接字函數時,套接字位址結構總是以引用形式(也就是以指向該結構的指針)來傳遞。然而以這樣的指針作為參數之一的任何套接字函數必須處理來自所支援的任何協定族的套接字位址格式。1982年采取的辦法是在sys/socket.h頭檔案中定義一個通用的套接字位址格式
strcut sockaddr{
unit8_t sa_len;
sa_family_t sa_family; //address family:AF_XXX value
char sa_data[]; //protocol-specific address
};
于是套接字函數被定義為以指向某個通用套接字位址結構的一個指針作為其參數之一,這正如bind函數的ANSI C函數原型所示:
int bind(int,struct sockaddr*,socklen_t);
這就要求對這些函數的任何調用都必須要将指向特定與于協定的套接字位址結構的指針進行類型強制轉換,變成指向某個通用套接字位址結構的指針,例如:
bind(sockfd,(strcut sockaddr*)&serv,sizeof(serv));
3. IPv6套接字位址結構
IPv6套接字位址格式定義在定義在netinet/in.h頭檔案
struct in6_addr{
unit8_t s6_addr[];//128-bit IPv6 address network byte order
};
struct sockaddr_in6{
unit8_t sin6_len; //length of this struct(28)
sa_family_t sin6_family; //AF_INET6
in_port_t sin6_port; //transport layer port
unit32_t sin6_flowinfo; //flow information undefined
strcut in6_addr sin6_addr; //IPv6 address
unit32_t sin6_scope_id //set of interfaces for a scope
};
對于具備範圍的位址,sin6_scope_id字段辨別其範圍,最常見的是鍊路局部位址的接口索引
三、值-結果參數
當往一個套接字函數傳遞一個套接字位址結構時,該結構總是以引用形式來傳遞,也就說說傳遞的是指向該結構的一個指針。該結構的長度也作為一個參數來傳遞,不過其傳遞方式取決于該結構的傳遞方向:從程序到核心、從核心到程序
1. 程序到核心
3個函數:bind、connect、sendto。這些函數的一個參數指向某個套接字位址結構的指針,另一個參數是該結構的整數大小
connect(sockfd,(struct sockaddr*)&serv,sizeof(serv));
-
核心到程序
4個函數:accept、recvfrom、getsockname、getpeername,這四個函數中其中兩個參數是指向套接字位址結構的指針和指向表示該結構大小的整數變量的指針
strcut sockaddr_un cli;
socklen_t len;
len=sizeof(cli);
getpeername(unixfd,(SA*)&cli,&len);
把套接字位址結構大小這個參數從一個整數改為指向某個整數變量的指針,其原因在于:當函數被調用時,結構大小是一個值,它告訴核心該結構的大小,這樣核心在寫該結構時不至于越界;當函數傳回時,結構大小又是一個結果,他告訴程序核心在該結構中究竟存儲了多少資訊
四、位元組排序函數
考慮一個16位整數,它由兩個位元組組成。記憶體中存儲這兩個位元組有兩種方法:一種是低序位元組存儲在起始位置,這稱為小端位元組序;另一種方法是高序位元組存儲在起始位址,這稱為大端位元組序,如圖3-8所示
給定系統所用的位元組序稱為主機位元組序
這裡寫代碼片(P64)
網絡協定必須指定一個網絡位元組序,作為網絡程式設計人員的我們必須清楚不同位元組序之間的差異。舉例來說,在每個TCP分節中都有16為的端口号和32為的IPv4位址。發送協定棧和接收協定棧必須就這些多位元組字段各個位元組的傳送順序達成一緻。網際協定使用大端位元組序傳送這些多位元組整數。兩種位元組序轉換常用的四個函數
#include<netinet/in.h>
unit16_t htons(unit16_t host16bitvalue);
unit32_t htonl(unit32_t host32bitvalue);
unit16_t ntohs(unit16_t net16bitvalue);
unit32_t ntohl(unit32_t net32bitvalue);
五、位元組操縱函數
操縱多位元組多字段的函數有兩組,名字以b開頭的第一組函數起源于4.2BSD,幾乎所有現今支援套接字函數的系統仍然支援它們,名字以mem開頭的第二組函數起源于ANSI C标準,支援ANSI C函數庫的所有系統都提供它們
#include<string.h>
void bzero(void *dest,size_t nbytes);
void bcopy(const void *src,void *dest,size_t nbytes);
int bcmp(const void *ptr1,const void *ptr2,size_t nbytes);
bzero把目标位元組串中指定數目的位元組置零,我們經常使用該函數把一個套接字位址結構初始化為0。
#include<string.h>
void* memset(void *dest,int c, size_t len);
void* memcpy(const void *dest,void *srcsize_t nbytes);
int memcmp(const void *ptr1,const void *ptr2,size_t nbytes);
六、inet_aton、inet_addr、inet_ntoa函數
inet_aton、inet_addr、inet_ntoa函數在點分十進制數串(“206.168.112.96”)與他的長度為32位的網絡位元組序二進制值間轉換IPv4位址
#include<arpa/inet.h>
int inet_aton(const char strptr,strcut in_addr *addrptr);//有效傳回1,否則傳回0
in_addr_t inet_addr(const char*strptr);//若字元串有效則為32位二進制網絡位元組序IPv4位址,否則為為INADDR_NONE(255.255.255.255),已被廢棄
char *inet_ntoa(struct in_addr inaddr);//傳回一個點分十進制數串的指針
七、inet_pton和inet_ntop函數
inet_pton和inet_ntop函數随着IPv6出現的新函數,對于IPv4和IPv6的位址都适用。函數名中的p和n分别代表表達(presentation)和數值(numeric)。位址的表達通常是ASCII字元串,數值格式則是存放到套接字位址結構中的二進制值
#include<arpa/inet.h>
int inet_pton(int family,const char *strptr,void *addrptr);//成功傳回1,輸入的不是有效的表達格式傳回0,出錯傳回-1
const char*inet_ntop(int family,const void* addrptr,char strptr,size_t len);//将數值格式addrptr轉換到表達式格式strptr,len參數是目标存儲單元的大小