二、套接字地址格式
大多數套接字函數都需要一個指向套接字地址結構的指針作爲參數。每個協議族都定義了自己的套接字地址結構,這些結構的名字均以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[8];//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[14]; //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[16];//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參數是目標存儲單元的大小