大多数套接字函数都需要一个指向套接字地址结构的指针作为参数,每个协议族都定义了它自己的套接字地址结构。这些结构的名字均以sockaddr_开头,并以对应每个协议地址族的唯一后缀结尾。
IPv4套接字地址结构以sockaddr_in命名,定义在<netinet/in.h>头文件中。POSIX定义为:
struct in_addr { in_addr_t s_addr; /*32位IPv4地址,网络字节序*/ } struct sockaddr_in{ uint8_t sin_len; /*无符号8位整数*/ sa_family_t sin_family; /*套接字地址结构的地址族*/ in_port_t sin_port; /*TCP或UDP端口,一般为uint16_t*/ struct in_addr sin_addr; char sin_zero[8];/*未使用*/ }sin_len字段表示结构体的长度,并不是所有的厂家都支持这个字段,而且POSIX规范也不要求有这个成员,所以即使有了长度字段,在Socket编程中也无需检测和设置这个字段,除非涉及路由套接字(由处理来自不同协议族的套接字地址结构的例程(如路由表处理代码)在内核中使用)。POSIX规范只需要s_addr,sin_family和sin_port这三个字段,但是几乎所有实现都增加了sin_zero字段,所以套接字地址结构大小都是至少16个字节。
in_addr_t数据类型是一个至少32位的无符号整数类型,in_port_t必须是一个至少16位的无符号整数类型,而sa_family_t可以是任何无符号整数类型,在支持长度字段的实现中,sa_family_t通常是一个8位的无符号整数,在不支持长度的字段实现中,则是一个长度为16位的无符号整数。
IPv4地址和TCP或UDP端口号在套接字地址结构中总是以网络字节序来存储。
32位的IPv4地址存在两种不同的访问方法。例如,若serv定义位某个网络套接字地址结构,那么serv.sin_addr将按in_addr结构引用其中的32位IPv4地址,而serv.sin_addr.s_addr将按in_addr_t(通常是一个无符号32位整数)来引用同一个32位IP地址。因此我们必须正确地使用IPv4地址,尤其是在将它作为函数的参数时,因为编译器对传递结构和传递整数的处理是完全不同的。
POSIX定义的一些数据类型如下:
当作为一个参数传递进任何套接字函数时,套接字地址总是以引用形式来传递,以这样指针作为参数的任何套接字函数必须处理来自所有支持的任何协议族的套接字地址结构,而各个厂商所定义的套接字结构又有区别,所以就定义了一个通用套接字地址结构struct sockaddr,这个结构定义在头文件<sys/socket.h>中:
struct sockaddr { uint8_t sa_len; sa_family_t sa_family; char sa_data[14]; }结构体struct sockaddr中,sa_len是结构体长度字段,sa_family表示地址协议族,还有一个有14个元素的字符数组sa_data。在有结构体长度的地址结构中,sa_family通常是一个8位无符号整数,那么结构体struct sockaddr的长度至少为16个字节。在套接字函数中,通常地址参数是一个指向struct sockaddr类型的指针,这也是为什么通常要对这个参数进行强制类型转换,如bind函数的ANSI C函数原型为:
int bind(int, struct sockaddr *, socklen_t);从网络应用程序开发的角度来看,这个通用的套接字地址结构的唯一用途就是对指向特定与协议的套接字地址结构的指针执行强制类型转换,但是在操作系统的内核中,内核必须使用struct sockaddr *类型来检查sa_family字段的值,从而确定这个结构的真实类型。
IPv6套接字的地址结构也在<netinet/in.h>头文件中定义,结构体定义代码如下:
struct in6_addr { uint8_t s6_addr[16];/*Ipv6的128位地址*/ } #define SIN6_LEN struct sockaddr_in6 { uint8_t sin6_len;/*结构体长度*/ sa_family_t sin6_family;/*协议族*/ in_port_t sin6_port;/*端口,网络字节序*/ uint32_t siin6_flowinfo;/*分成两个字段,低序20位是流标,高序12位保留*/ struct in6_addr sin6_addr;/*IPv6的128位地址,网络字节序*/ uint32_t sin6_scope_id; /*标识具备范围的地址,最常见的是链路局部地址的结构索引*/ }如果系统支持套接字地址结构,那么SIN6_LEN常值必须定义。
《UNIX网络编程 卷1:套接字联网API(第3版)》