在网络通讯程序中会用到sockaddr和sockaddr_in这两个结构体,下面对这两个结构体进行一下分析。
一、 sockaddr和sockaddr_in
struct sockaddr 这个结构体是在<<sys/socket.h>>头文件中定义的。结构体的定义如下:
struct sockaddr
{
sa_family_t sin_family; //地址族
char sa_data[14]; //14字节,包含套接字中的目标地址和端口信息
};
这个结构体中定义了地址族和14个字节的数据,数据中包含套接字中的目标地址和端口信息。这个结构体的缺点是没有用特定的变量来区分地址和端口。
struct sockaddr_in 这个结构体是在<<netinet/in.h>>头文件中定义的。结构体的定义如下:
struct sockaddr_in
{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
uint16_t sin_port; //16位的端口号
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用,一般用0填充
};
其中
struct in_addr
{
in_addr_t s_addr; //32位的IP地址
};
sockaddr_in结构体与sockaddr 结构体的内容是一致的,而且可以相互转换。sockaddr_in结构体中将端口号和地址做了区分,可以更方便用户进行变量的读写。sockaddr则常用于bind、connect、recvfrom、sendto等函数的参数,指明地址信息,是一种通用的套接字地址。所以在程序中通常会采用sockaddr_in进行读写操作,然后转换为sockaddr进行绑定、连接等操作。比如下面的代码。在这段代码中server_addr为sockaddr_in类型的结构体,对这个变量进行相应赋值后,调用bind()函数将server_addr与socket进行绑定,在bind()函数的形参中,将server_addr转换成了sockaddr 类型的结构体。
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(portnumber);
if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)
{
fprintf(stderr,"Bind error: %s\n\a",strerror(errno));
exit(1);
}
二、inet_ntoa()和inet_addr()
inet_ntoa()和inet_addr()这两个函数用来进行IP地址的转换。
其中 inet_ntoa()的完整声明为char *inet_ntoa (struct in_addr);
函数的形参为struct in_addr,它实际上是一个32位的IP地址。这个函数的作用是将32位的IP地址转换为字符串形式的IP地址。如下所示代码
struct in_addr adr;
adr.s_addr=0x0200A8C0;
printf("IP is: %s\n",inet_ntoa(adr));
执行的结果为
IP is: 192.168.0.2
adr.s_addr=0x0200A8C0;这一句给IP地址复制,它有4个字节组成,这4个字节拆分开转换为10进制为:2、0、168、192,所以转换为字符串为上述的字符串,之所以顺序是反的是因为网络传输过程是大端模式。
inet_addr()函数的完整声明为in_addr_t inet_addr(const char* cp);
函数发输入为IP地址的字符形式,输出为32位的IP地址,如下所示代码
in_addr_t adr_x;
adr_x = inet_addr("192.168.0.100");
printf("IP address is: 0x%4x\n",adr_x);
执行的结果为
IP address is: 0x6400a8c0
执行结果是将字符串形式的IP地址192.168.0.100,转换成了32位的IP地址。