常用套接字地址结构:
1.IPv4套接字地址结构
#include <netinet/in.h> 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,network byte ordered struct in_addr sin_addr; // 32-bit IPv4 address, network byte ordered char sin_zero[8]; // unused };
其中,sin_len字段表示长度,一般为uinit32_t,并非所有厂家都支持这个字段,所以套接字函数都有个表示长度的参数。
sin_family表示地址族。
sin_port表示端口号,一般为uint16_t,网络字节序。
sin_addr表示IPv4地址。structin_addr的POSIX定义如下:
struct in_addr { in_addr_t s_addr; };
in_addr_t一般为uint_32,网络字节序。
2.旧版套接字地址结构#include <sys/socket.h> struct sockaddr { uint_8 sa_len; sa_family_t sa_family; // address family: AF_xxx value char sa_data; // protocol-specific address };
很多套接字函数使用这个结构作为形参,调用的时候将该位置的实参强制转换为通用套接字结构。该套接字没用处,唯一的用处就是对指向特定于协议的套接字地址结构的指针执行类型强制转换。
3.IPv6套接字地址结构
#include <netinet/in.h> #define SIN6_LEN //required for compile-time tests struct sockaddr_in6 { uint8_t sin6_len; // length of this struct (28) sa_family_t sin6_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 };其中struct in6_addr的定义为
struct in6_addr { unit8_t s6_addr[16]; //128-bit IPv6 address, network byte ordered };4.新的通用套接字地址结构
旧版的通用套接字地址结构在设计之时,IPv6尚未出现,已经不能满足像IPv6这样的新的地址族,所以新的通用套接字地址结构应运而生。
struct sockaddr_storage { 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 the system supports. * b) enough storage to hold any type of socket address that the system supports. */ };其实新版的通用套接字地址结构就明确定义了两个元素:长度和地址族。另外的部分根据不同的应用场合而变。所以规定sockaddr_storage结构必须类型强制转换成或复制到适合于ss_family字段给出的地址类型的套接字地址结构中,才能访问其他字段。如果说这样还不足以说明,书上的一个插图可以很好的说明通用套接字地址结构的样子。
图中可以看到,sockaddr_storage的前四个字节跟其他的地址结构一样,而后面的部分足够大,可以容纳操作系统接受的最大的地址结构,使用的时候,要把该结构强制转换或复制成与AF_XXX字段指示的地址结构,然后可以用转换(或复制)后的地址结构访问后面的字段。
套接字函数接受的参数总包括这样两个:一个套接字地址结构,一个表示地址结构的长度。前者总是以引用形式(也就是指针)传递,后者却分两种情况,
进程->内核:长度值以“值”的形式传递
内核->进程:长度值以引用(也就是指针)的形式传递。
因为从进程到内核传递的时候,内核值需要知道要传入的结构的大小就可以了,所以是一种按值传递的形式。如下图
从进程到内核传递地址结构的函数有:bind、connect和sendto
要从内核到进程传递递质结构,当函数被调用时,结构大小是一个值(value),告诉内核该结构的大小,这样内核在写该结构时不至于月结;当函数返回时,结构大小有时一个结果(result),告诉进程内核在该结构中究竟存储了多少信息。这种类型的参数成为值-结果(value-result)参数。如下图所示
从内核到进程传递地址结构的函数有:accept、recvfrom、getsockname和getpeername。
字节序排序函数
网络字节序是大端模式,主机字节序不一定。两者要进行转换,定义了如下函数:
#include <netinet/in.h> uint16_t htons(uint16_t host16bitvalue); uint32_t htonl(uint32_t host32bitvalue); 返回值:网络字节序的值 uint16_t ntohs(uint16_t net16bitvalue); uint32_t ntohl(uint32_t net32bitvalue); 返回值:主机字节序的值地址表示形式转换函数
1.适用于IPv4的旧版函数
#include <arpa/inet.h> int inet_aton(const char *strptr, struct in_addr *addrptr); 返回:字符串有效返回1,否则返回0 in_addr_t inet_addr(const char *strptr); //这个函数已经被废了 返回:若有效返回32为二进制网络字节序的IPv4地址,否则为INADDR_NONE char *inet_ntoa(struct in_addr inaddr); 返回:指向一个点分十进制数串的指针估计命名中a表示address,即字符串形式的地址,n表示numeric,即网络字节序的数值地址,一般是个地址结构。
#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); 返回:成功返回指向结果的指针,出错返回NULL 其中len参数可使用一下宏: #include <netinet/in.h> #define INET_ADDRESTRLEN 16 // for IPv4 dottd-decimal #define INET6_ADDRSTRLEN 46 // for IPv6 hex string命名中p表示表达(presentation),即字符串形式的地址,n表示数值(numeric),即网络字节序的数值地址。后面定义的两个宏可以用于inet_ntop函数的len参数,避免函数溢出。格式转换函数可以总结为下图: