通用地址结构体
用socket进行网络编程时,首先需要知道要通信另一方的地址,linux中定义了sockaddr结构体来表示socket地址:
#inlcude <bits/socket.h>
struct sockaddr
{
sa_family_t sa_family;
char sa_data[14];
};
struct sockaddr_storage
{
sa_family_t sa_family;
unsigned long int __ss_align;
char __ss_padding[128-sizeof(__ss_align)];
};
其中sa_family表示的是地址的协议族。创建socket时需要指定使用哪种协议族,常见的协议族以对应地址族的关系:
协议族 | 地址族 | 描述 | 地址示例、存储长度 |
PF_UNIX | AF_UNIX | UNIX本地域协议族 | 如 unix:/var/php-fpm.sock 文件的路径名,长度可达108字节 |
PF_INET | AF_INET | TCP/IPv4协议族 | 如 127.0.0.1:80 16bit端口号、32bit IPv4地址 |
PF_INET6 | AF_INET6 | TCP/IPv6协议族 | 如 [1fff:0:a88:85a3::ac1f]:8001 16bit端口号、32bit流标识、128bit IPv6地址、32bit范围ID,共26字节 |
上面 PF_* 和 AF_* 定义在 bits/socket.h 头文件中,前后两种宏有相同的值,通常是可以混用。
sockaddr.sa_data用来存放socket地址值,只有14字节,无法完全容纳多数协议族地址值,所以定义了上例中的第二个结构体:sockaddr_storage,__ss_padding成员用来存储地址值。
各协议族专用结构体
仔细看前面的通用地址结构体可以注意到,只有一个字段来存储地址值。像IPv4、IPv6协议地址中包括ip和端口号,都存储在一个字段中,每次使用还要来回解析,过于烦琐,所以Linux针对各个协议族定义了各自专用的socket地址结构体。
#include <sys/un.h>
// unix域
struct sockaddr_un
{
sa_family_t sun_family;
char sun_path[108];
};
// IPv4
struct sockaddr_in
{
sa_family_t sin_family; // 地址族:AF_INET
u_int16_t sin_port; // 端口号,用网络字节序表示
struct in_addr sin_addr; // IPv4地址结构体
};
struct in_addr
{
u_int32_t s_addr; // IPv4地址,用网络字节序表示
};
// IPv6
struct sockaddr_in6
{
sa_family_t sin6_family; // 地址族:AF_INET6
u_int16_t sin6_port; // 端口号,用网络字节序表示
u_int32_t sin6_flowinfo; // 流信息,应设置为0
struct in6_addr sin6_addr; // IPv6地址结构体
u_int32_t sin6_scope_id; // scope ID,尚处于实验阶段
};
struct in6_addr
{
unsigned char sa_addr[16]; // IPv6地址,用网络字节序表示
};
因为socket相关接口使用的地址结构体是sockaddr,所以这些专用socket地址在使用时需要转成sockaddr类型,强制转换即可。
IP地址转换函数
#include <arpa/inet.h>
// IPv4函数
in_addr_t inet_addr(const char *strptr);
int inet_aton(const char *cp, struct in_addr *inp);
char* inet_ntoa(struct in_addr in);
// 同时适用于IPv4、IPv6地址函数
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);
示例:
#include <stdio.h>
#include <arpa/inet.h>
void main() {
// inet_addr() 把点分十进制字符串形式的ip地址转换成网络字节序整数表示IPv4地址
// 失败时返回返回 INADDR_NONE (-1)
char *ip_str = "127.0.0.1";
in_addr_t ip_int = inet_addr(ip_str);
if (INADDR_NONE == ip_int) {
printf("bad ip\n");
} else {
printf("%d\n", ip_int); // 16777343
}
// inet_aton() 把字符串ip转成in_addr类型的地址,第二个参数保存转换后的ip
// 失败返回0,成功返回1
struct in_addr ip_addr;
int succ = inet_aton(ip_str, &ip_addr);
if (0 == succ) {
printf("bad ip\n");
} else {
printf("%d\n", ip_addr.s_addr); // 16777343
}
// 把in_addr类型的结构转成字符串ip
char * ip_str2 = inet_ntoa(ip_addr);
printf("%s\n", ip_str2); // 127.0.0.1
}
点击下方阅读原文,或访问 daemoncoder.com 发现更多优质内容