linux socket编程中经常用到各种类型的地址, 最近在一些开源代码中经常见到, 它们是进行socket编程的基础, 本文对常见的地址结构进行简单整理, 并在附录中对某些函数给出了helloworld式的测试代码, 方便查询.
IPV4地址结构
in_addr
这个结构内部以网络字节序的32位无符号整数表示的ipv4地址, 在后续介绍的地址结构中会用到这个结构. 之所以一个整数也要放在一个结构体中来代表地址, 是历史原因. 一开始这个结构内部不仅仅是一个整数, 后来演化成只有一个整数, 但是这个结构体保留了. 所以要引用一个IPV4地址, 可以传该结构体, 也可以传结构体中的整数, 这需要根据具体接口的要求正确使用.
struct in_addr {
in_addr_t s_addr; /* 32-bit IPv4 address */
/* network byte ordered */
};
sockaddr_in
这个是一个常见的IPV4地址结构, 注释中给出了各个字段的含义与大小. 其中sin_family, sin_port, 以及sin_addr是POSIX规范要求的三个字段, 其他的为额外添加字段. 在POSIX类型中, in_addr_t必须至少是一个32位无符号整数, in_port_t至少是一个16位无符号整数类型, sa_family_t则可以是任意无符合整数类型.
可以看到, POSIX定义了规范定义了各个结构必须有的字段, 以及字段的大小.
struct sockaddr_in {
uint8_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 */
};
sockaddr
通用套接字结构, 起来void*的作用, 在定义这个结构之前, ANSI C还没有出来, 还没有void*, 这个结构是为了能够处理任意格式的地址参数, 通过指针的方式来传递, 对于socket编程而言, 地址结构作为参数传递的时候一般是通过指针进行的, 而不是直接传递结构体. 结构体在本机内部使用, 而不会在机器直接传递.有了这个结构体, 在接口编写的时候, 要求的参数是struct sockaddr*类型, 到了函数内部通过类型转换变成具体的地址结构类型.
struct sockaddr {
uint8_t sa_len;
sa_family_t sa_family; /* address family: AF_xxx value */
char sa_data[14]; /* protocol-specific address */
};
IPV6地址结构
in6_addr
这个结构使用128bit的空间直接存储一个ipv6的地址.
struct in6_addr {
uint8_t s6_addr[16]; /* 128-bit IPv6 address */
/* network byte ordered */
};
sockaddr_in6
实际使用的IPV6地址.
#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 */
};
sockaddr_storage
这个结构和sockaddr的区别在于, 足够大, 能够存储任何系统支持的地址结构,且满足任意的对齐要求, 使用的时候, 需要强制类型转化到特定需要的地址结构. 也是通过传指针的方式来使用的.
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.
*/
};
addrinfo
这个结构可以通过函数getaddrinfo来获取,一方面可以作为getaddrinfo函数调用时候的过滤器, 另一方面可以装getaddrinfo返回的结果(通过参数列表中的指针返回). 可以看到, 其是一个链表结构, 其结构成员如下.
struct addrinfo {
int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc.
int ai_family; // AF_INET, AF_INET6, AF_UNSPEC
int ai_socktype; // SOCK_STREAM, SOCK_DGRAM
int ai_protocol; // use 0 for "any"
size_t ai_addrlen; // size of ai_addr in bytes
struct sockaddr *ai_addr; // struct sockaddr_in or _in6
char *ai_canonname; // full canonical hostname
struct addrinfo *ai_next; // linked list, next node
};
我们接下来通过getadd