在Unix/Linux网络编程中,会涉及到很多的数据结构,下面来总结一下:
IPv4相关结构:
struct in_addr
{
in_addr_t s_addr; //表示32位的IP地址,32位无符号整型
}
struct sockaddr_in
{
uint8_t sin_len; //表示该结构体的长度,8位无符号整型
sa_family_t sin_family; //表示套接口使用的协议族,8位无符号整型
in_port_t sin_port; //表示套接口使用的端口号,16位无符号整型
struct in_addr sin_addr; //表示IP地址,32位无符号整型
char sin_zero[8]; //该成员基本不使用,总是置为0
}
对于POSIX规范来说,sin_len成员是不要求一定存在的,即便这个成员存在,也无需设置它或者检查它。换句话说就是一般情况下,我们用不到这个成员。
对于POSIX规范来说,sin_family,sin_addr,sin_port这三个成员是必须的。并且几乎所有的实现都增加了sin_zero成员。
sin_family的类型与sin_len成员有关,如果结构体中定义了成员sin_len,那么sin_family一般就是8位无符号整型。如果结构体中没有定义成员sin_len,那么sin_family一般就是16位无符号整型,这样一来整个结构体的大小至少是16字节(1+1+2+4+8)。
IPv6相关结构:
struct in6_addr
{
unit8_t s6_addr[16]; //表示128位的IP地址,这里采用数组的形式
}
struct sockaddr_in6
{
uint8_t sin6_len; //表示该结构体的长度,8位无符号整型
sa_family_t sin6_family; //表示套接口使用的协议族,8位无符号整型
in_port_t sin_port; //表示套接口使用的端口号,16位无符号整型
uint32_t sin_flowinfo; //低序20位是流标签,高序12位保留
struct in6_addr sin6_addr //表示128位的IP地址
uint32_t sin6_scope_id; //标识对于具备范围的地址而言有意义的范围
}
同样的,sin6_family的类型与sin6_len是否定义有关,在Ubuntu中,并没有定义成员sin6_len,其成员sin6_family是16位无符号整型。并且结构中的成员是有序排列的。
通用套接口地址结构:
struct sockaddr
{
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14]; //表示14字节的协议地址
}
当作为参数传递给任一个套接口函数时,套接口地址结构总是通过指针来传递,但通过指针来取得此参数的套接口函数必须处理来自所支持的任何协议族的套接口地址结构。因此通过定义sockaddr来获取不同的套接口地址结构。
新的通用套接口地址结构:
struct sockaddr_storage
{
uint8_t ss_len; //表示该结构的长度
sa_family_t ss_family; //表示协议族
}
sockaddr_storage是作为IPv6套接口API的一部分而定义的新的通用套接口地址结构,它足以容纳系统所支持的任何套接口地址结构,而sockaddr可能力有未逮。
sockaddr_storage与sockaddr的差别:
1.如果系统支持的任何套接口地址结构有对齐需求,那么sockaddr_storage能够满足最苛刻的对齐要求。
2.sockaddr_storage足够大,能够容纳系统支持的任何套接口地址结构。
除了ss_family和ss_len两个成员外,sockaddr_storage结构中的字段(如果有的话)对用户来说是透明的。sockaddr_storage必须类型强制转换或拷贝到适合于ss_family字段所给出地址类型的套接口地址结构中,才能访问其他字段。通俗的说就是sockaddr_storage中还有其他数据,只不过这些数据不可见(没有为其显式的定义成员),必须通过其他途径来获取这些数据。
为了处理变长的结构,任何时候当我们把指向套接口地址结构的指针作为参数传递给套接口函数时,也把该结构的长度作为另一个参数传递给函数,从bind,accept等函数中就可以看出这个特点。