socket
通用socket
表示socket地址的结构体sockaddr定义如下
struct sockaddr
{
__SOCKADDR_COMMON (sa_); //地址族类型变量
char sa_data[14]; //socket地址值
};
不同协议族的地址值具有不同的含义和长度,有些地址值的长度可以达到108字节(PF_UNIX)。14字节的sa_data无法容纳这些协议族的地址值。因此linux定义了下面这个新的通用socket地址结构体:
#define __ss_aligntype unsigned long int
#define _SS_PADSIZE (_SS_SIZE - __SOCKADDR_COMMON_SIZE - sizeof(__ss_aligntype))
struct sockaddr_storage
{
__SOCKADDR_COMMON (ss_);
char __ss_padding[_SS_PADSIZE];
__ss_aligntype __ss_align;
};
这个结构体不仅提供了足够大的空间用于存放地址值,而且是内存对齐的(__ss_align的作用)
专用socket地址
上面两个通用socket地址结构体很不好用,比如设置与获取IP地址和端口号就需要执行繁琐的位操作。所以linux为各个协议族提供了专门的socket地址结构体。
struct sockaddr_un
{
__SOCKADDR_COMMON (sun_); //地址族
char sun_path[108]; //文件路径名
};
TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用socket地址结构体,它们分别用于IPv4和IPv6:
#include<netinet/in.h>
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_); //地址族
in_port_t sin_port; //端口号 用网络字节序表示
struct in_addr sin_addr; //IPv4地址结构体
unsigned char sin_zero[sizeof(struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof(in_port_t) - sizeof(struct in_addr)];
};
struct in_addr
{
uint32_t s_addr; //IPv4地址,要用网络字节序表示
};
struct sockaddr_in6
{
__SOCKADDR_COMMON (sin6_); //地址族
in_port_t sin6_port; //端口号
uint32_t sin6_flowinfo; //流信息,应设置为0
struct in6_addr sin6_addr; //IPv6地址结构体
uint32_t sin6_scope_id; //scope ID
};
struct in6_addr
{
uint8_t __u6_addr8[16]; //IPv6地址,使用网络字节序
};
所有专用socket地址(包括sockaddr_storage)类型的变量在实际使用时都需要转化为通用socket地址类型sockaddr(强制转换即可),因为所有socket编程接口使用的地址参数的类型都是sockaddr。
IP地址转换函数
下面三个函数可用于用点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间的转换:
#include<arpa/inet.h>
in_addr_t inet_addr(const char* __cp);
int inet_aton(const char *__cp,struct in_addr *__inp);
char* inet_ntoa(struct in_addr __in);
inet_addr函数将用点分十进制字符串表示的IPv4地址转化为用网络字节序整数表示的IPv4地址。失败时返回INADDR_NONE。
inet_aton函数完成和inet_addr同样的功能,但是将转化结果存储于参数inp指向的地址结构中。成功是返回1,失败时返回0。
inet_ntoa函数将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址。但是需要注意的是,该函数内部用一个静态变量存储转化结果,函数的返回值指向该静态内存,因此inet_ntoa是不可重入的。
下面这对函数也能完成和前面三个函数同样的功能,并且同时支持IPv4和IPv6:
#include<arpa/inet.h>
int inet_pton(int __af,const char *__restrict __cp,void *__restrict __buf);
const char *inet_ntop(int __af,const void *__restrict __cp,char *__restrict __buf,socklen_t __len);
inet_pton函数将用字符串表示的IP地址__cp(用点分十进制字符串表示的IPv4或是用十六进制字符串表示的IPv6地址)转化成用网络字节序整数表示的IP地址,并把转化结果存储于__buf指向的内存中。其中,af参数指定地址族,可以是AF_INET或者AF_INET6。inet_pton成功时返回1,失败时返回0并设置errno。
inet_ntop函数进行相反的转化,前三个参数的含义于inet_pton参数相同,最后一个参数__len指定目标存储单元的大小。inet_ntop成功时返回目标存储单元的地址,失败时返回NULL并设置errno。