sockaddr与sockaddr_in及其使用
编了有不少的socket相关的例子了,同时在现在的项目中常用到socket编程,今天突然发现对与socket编程中的地址使用还不是很熟练,于是综合一下网上的资料,详细探讨下.首先要说明的是这里主要探讨struct sockaddr 与struct sockaddr_in两个结构体.在linux环境下,结构体struct sockaddr在/usr/include/linux/socket.h中定义,具体定义如下:
typedef unsigned short sa_family_t; struct sockaddr { sa_family_t sa_family; /* 地址族,一般都是“AF_xxx”的形式,通常用的是AF_INET,2个字节 */
char sa_data[14]; /* 14字节的协议地址,包含该socket的IP地址和端口等信息 */
};
这是通用socket地址(共16字节).具体到internet环境下使用的socket的地址为sockaddr_in,二者长度一样,都是16个字节.二者可以进行类型转换.一般情况下,需要把sockaddr_in结构强制转换成sockaddr再传入系统调用函数中.
struct sockaddr_in { short int sin_family; /* 地址族,形如AF_xxx,通常用的是AF_INET,2字节 */ unsigned short int sin_port; /* 端口号(使用网络字节顺序)2字节 */ struct in_addr sin_addr; /* 存储IP地址,4字节,就是32位的ip地址 */ unsigned char sin_zero[8]; /* 总共8个字节,实际上没有什么用,只是为了和struct sockaddr保持一样的长度 */ };
struct in_addr其实就是32位IP地址,下面是in_addr的结构:
struct in_addr { unsigned long s_addr; };
还有另一种形式,如下:
struct in_addr { union { struct{unsigned char s_b1,s_b2,s_b3,s_b4;} S_un_b; struct{unsigned short s_w1,s_w2;} S_un_w; unsigned long S_addr;//4字节,32位,按照网络字节顺序存储IP地址 } S_un; };
关于网络字节顺序:其实数据的顺序是由cpu决定的,与操作系统无关,如 Intel x86结构下,short型数0x1234表示为34 12,int型数0x12345678表示为78 56 34 12(小端数据),如IBM power PC结构下,short型数0x1234表示为12 34,int型数0x12345678表示为12 34 56 78,则为大端数据.在网络传输时需要做好转换,网络字节顺序为大端字节顺序.下面是一些用于转换的函数.
htons:把unsigned short类型从主机序转换到网络序;
htonl:把unsigned long 类型从主机序转换到网络序;
ntohs:把unsigned short类型从网络序转换到主机序;
ntohl:把unsigned long 类型从网络序转换到主机序;
inet_aton(const char *string, struct in_addr*addr):将一个字符串IP地址转换为一个32位的网络序列IP地址
inet_addr:是将一个点分制的IP地址(如192.168.0.1)转换为上述结构中需要的32位IP地址(0xC0A80001),即转换成in_addr,inet_addr()返回的地址已经是网络字节格式,所以无需再调用函数htonl();
inet_ntoa(struct in_addr):返回点分十进制的字符串在静态内存中的指针,所以每次调用 inet_ntoa(),它就将覆盖上次调用时所得的IP地址.
inet_pton(int af, const char *src, void *dst):函数将点分十进制的地址src转换为in_addr的结构体,并复制在dst中.
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt):转换网络二进制结构到点分十进制类型的地址
例子:
my_addr.sin_family = AF_INET; /* 主机字节序 */ my_addr.sin_port = htons(MYPORT); /* short, 网络字节序 */ my_addr.sin_addr.s_addr = htonl(INADDR_ANY); //还有如此的格式 my_addr.sin_addr.s_addr = inet_addr("192.168.0.1");
一般编程中并不直接针对sockaddr操作,而是使用sockaddr_in来进行操作.要做转换的时候用:(struct sockaddr*)mysock_addr,填值的时候使用sockaddr_in结构,而作为函数的参数传入的时候转换成sockaddr结构就行了.