一 点睛
socket地址分为通用socket地址和专用socket地址。
通用socket地址出现在一些socket api函数中,比如bind函数、connect函数等。
专用socket地址是为了方面使用而提出来的。
两者可以相互转换。
二 通用socket地址
通用socket地址是一个结构体,名字是sockaddr,定义在/usr/include/bits/socket.h中。
#include <bits/sockaddr.h>
/* Structure describing a generic socket address. */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
宏展开后的形式是
struct sockaddr
{
sa_family_t sin_family; //sa_family_t是unsigned short int
char sa_data[14];
};
sin_family用来存放地址族(或协议族)类型,常用取值如下:
-
PF_UNIX:UNIX本地域协议族
-
PF_INET:IPv4协议族
-
PF_INET6:IPv6协议族
-
AF_UNIX:UNIX本地域地址族
-
AF_INET:IPv4地址族
-
AF_INET6:IPv6地址族
sa_data用来存放地址数据。
由于sa_data只有14个字节,随着时代的发展,一些新的协议提出来了,比如IPv6,它的地址长度不够14个字节,不同协议族的具体地址长度如下表:
协议族 | 地址含义和长度 |
PF_UNIX | 文件全路径名,最大长度可达108字节。 |
PF_INET | 32位IPv4地址和16位端口号,共6个字节。 |
PF_INET6 | 128位IPv6地址、16位端口号、32位流标识和32位范围ID,共26字节。 |
sa_data太小了,容纳不下了,怎么办?LInux定义了新的存储结构:
struct sockaddr_storage
{
sa_family_t sa_family;
unsigned long int __ss_align;
char __ss_padding[128-sizeof(__ss_align)];
};
三 专用socket地址
通用地址结构把IP地址和端口号信息都放到一个char数组中,使得使用起来不方便。为此,Linux为不同的协议族定义了不同的socket结构体。
1 IPv4的socket地址定义,定义于/usr/include/netinet/in.h
struct sockaddr_in
{
sa_family_t sin_family; //地址族,取AF_INET
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)];
};
in_addr定义如下:
struct in_addr
{
in_addr_t s_addr; //存放ipv4地址,用网络字节序表示,in_addr_t就是一个无符号的32位整型
};
2 IPv6的socket地址定义,定义于/usr/include/netinet/in.h
struct sockaddr_in6
{
sa_family_t sin6_family; //地址族,取AF_INET6
in_port_t sin6_port; /* 端口号,用网络字节序表示 */
uint32_t sin6_flowinfo; /* IPv6 流信息,设置为0n */
struct in6_addr sin6_addr; /* IPv6 地址 */
uint32_t sin6_scope_id; /* IPv6 id范围 */
};
in6_addr定义如下:
struct in6_addr
{
union
{
uint8_t __u6_addr8[16];
#if defined __USE_MISC || defined __USE_GNU
uint16_t __u6_addr16[8];
uint32_t __u6_addr32[4];
#endif
} __in6_u;
#define s6_addr __in6_u.__u6_addr8
#if defined __USE_MISC || defined __USE_GNU
# define s6_addr16 __in6_u.__u6_addr16
# define s6_addr32 __in6_u.__u6_addr32
#endif
};
3 UNIX本地域协议族定义,定义于/usr/include/linux/un.h
#define UNIX_PATH_MAX 108
struct sockaddr_un {
__kernel_sa_family_t sun_family; /* 地址族,取AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* 文件全路径名*/
};
这些专用的socket地址结构显然比通用的socket地址更清楚,把各个信息用不同的字段来表示。但要注意的是,socket api函数使用的是通用的地址结构,因此我们具体使用的时候,最终要把专用地址结构转换为通用地址结构,不过可以强制转换。
四 IP地址的转换
1 点睛
函数inet_addr将点分十进制IP地址转换为二进制地址
函数inet_aton将点分十进制IP地址转换为二进制地址
函数inet_ntoa将二进制地址转换为点分十进制IP地址
2 代码
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, const char * argv[])
{
struct in_addr ia;
inet_aton("172.16.2.6", &ia);
printf("ia.s_addr=0x%x\n",ia.s_addr);
printf("real_ip=%s\n",inet_ntoa(ia));
return 0;
}
3 运行
[root@localhost test]# g++ -o test test.cpp[root@localhost test]# ./testia.s_addr=0x60210acreal_ip=172.16.2.6