1. 三种常见结构类型
在套接字编程编程中,有三种常见的结构类型,它们用来存放socket地址信息。这三种结构类型分别为struct in_addr、struct sockaddr、struct sockaddr_in,对这三种结构类型说明如下。
struct in_addr专门用来存储IP地址,对于IPv4来说,IP地址为32位无符号整数。其定义如下:
struct in_addr {
unsigned long s_addr;
};
struct sockaddr结构用来保存套接字地址信息,其定义如下:
struct sockaddr {
unsigned short sa_family; /* 地址族, AF_xxx */
char sa_data[14]; /* 14字节的协议地址*/
};
struct sockaddr结构中sa_family成员说明的是地址族类型,一般为“AF_INET”;而sa_data则包含远程主机的IP地址和端口等信息。
struct sockaddr结构类型使用在socket的相关系统调用函数中,但这个结构sa_data字段可以包含较多信息,不利于方便编程和对其进行赋值,因此建立了struct sockaddr_in结构,该结构与struct sockaddr结构大小相等,能更好处理struct sockaddr结构中的数据。对struct sockaddr_in结构变量进行赋值完成后,在进行socket相关系统调用时,再将struct sockaddr_in结构变量强制转换为struct sockaddr结构类型。
struct sockaddr_in结构定义如下:
struct sockaddr_in { /*“in” 代表“Internet”*/
short int sin_family; /* Internet地址族*/
unsigned short int sin_port; /* 端口号*/
struct in_addr sin_addr; /* Internet地址*/
unsigned char sin_zero[8]; /* 填充0(为了保持和struct sockaddr一样大小)*/
};
在实际应用编程中,对套接字地址结构使用方法和流程如下:
① 首先,定义一个sockaddr_in结构变量,并将它初始化为0,代码如下:
struct sockaddr_in myad;
memset(&myad,0,sizeof(struct sockaddr_in));
② 然后,给这个结构变量赋值,代码如下:
myad.sin_family=AF_INET;
myad.sin_port=htons(8080);
myad.sin_addr.s_addr=htonl(INADDR_ANY);
③ 在进行函数调用时,将这个结构强制转换为struct sockaddr类型,代码如下:
bind(serverFd, (struct sockaddr *)& myad, sizeof(myad))
2. 整型字节序转换函数
为保证“大端”和“小端”字节序的机器之间能相互通信,需在发送多字节整数时,将主机字节序转换成网络字节序,或将网络字节序转换为主机字节序。下图说明了网络字节序与小端字节序、大端字节序的对照关系。字节转换主要是针对整型进行转换,字符型由于是单字节,所以不存在这个问题。整型字节序转换函数函数原型及其说明如下表所示。
所需头文件 | #include <netinet/in.h> |
函数说明 | 完成网络字节序与主机字节序的转换 |
函数原型 | uint16_t htons(uint16_t hostshort) /*短整型主机转换为网络字节序*/ uint32_t htonl(uint32_t hostlong) /*长整型主机转换为网络字节序*/ uint16_t ntohs(uint16_t netshort) /*短整型网络转换为主机字节序*/ uint32_t ntohl(uint32_t netlong) /*长整型网络转换为主机字节序*/ |
函数传入值 | hostshort、hostlong:为转换前主机字节序数值 netshort、netlong:为转换前网络字节序数值 |
函数返回值 | ① htons、htonl返回转换后网络字节序数值 ② ntohs、ntohl返回转换后主机字节序数值 |
附加说明 | h表示主机,n表示网络,s表示短整数,l表示长整数,to表示转换 |
3. IP地址转换函数
IP地址转换函数是指完成点分十进制IP地址与二进制IP地址之间的相互转换。IP地址转换主要有inet_aton、inet_addr和inet_ntoa这三个函数完成,这三个地址转换函数都只能处理IPv4地址,而不能处理IPv6地址。这三个函数的函数原型及其具体说明如下列表格所示。
inet_addr(将点分十进制IP地址转换为二进制地址) | |
所需头文件 | #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> |
函数说明 | 将点分十进制IP地址转换为二进制地址 |
函数原型 | in_addr_t inet_addr(const char *cp) |
函数传入值 | cp:点分十进制IP地址,如“10.10.10.1” |
函数返回值 | 成功:返回二进制形式的IP地址 |
失败:返回一个常值INADDR_NONE(32位均为1) |
inet_aton(将点分十进制IP地址转换为二进制地址) | |
所需头文件 | #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> |
函数说明 | 将点分十进制IP地址转换为二进制地址 |
函数原型 | int inet_aton(const char *cp, struct in_addr *inp) |
函数传入值 | cp:点分十进制IP地址,如“10.10.10.1” |
函数传出值 | inp:转换后二进制地址信息保存在inp中 |
函数返回值 | 成功:非0 |
失败:0 |
inet_ntoa(将二进制地址转换为点分十进制IP地址) | |
所需头文件 | #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> |
函数说明 | 将二进制地址转换为点分十进制IP地址 |
函数原型 | char *inet_ntoa(struct in_addr in) |
函数传入值 | in:二进制IP地址 |
函数返回值 | 成功:返回字符串指针,此指针指向了转换后的点分十进制IP地址 |
失败:NULL |
4. 获取主机信息函数
(1)getpeername函数原型
getpeername(获取通信对方信息) | ||
所需头文件 | #include <netinet/in.h> | |
函数说明 | 获取通信对方信息 | |
函数原型 | int getpeername(int s, struct sockaddr *name, socklen_t *namelen) | |
函数传入值 | s:所连接的socket文件描述符 | |
namelen:返回name的长度,可以设置为sizeof(struct sockaddr) | ||
函数传出值 | name:指向包含对方IP地址、端口等信息的sockaddr结构地址 | |
函数返回值 | 成功 | 0 |
失败 | -1,失败原因存于error中 |
(2)gethostname函数原型
gethostname(获取主机名称) | ||
所需头文件 | #include <netinet/in.h> | |
函数说明 | 获取通信对方信息 | |
函数原型 | int gethostname(char *name, size_t len) | |
函数传入值 | len:name数组的长度 | |
函数传出值 | name:主机的名称 | |
函数返回值 | 成功 | 0 |
失败 | -1,失败原因存于error中 |
(3)gethostname函数举例
gethostname.c源代码如下:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
char hostname[30] ;
int flag =0 ;
memset(hostname, 0x00, sizeof(hostname));
flag = gethostname(hostname, sizeof(hostname));
if( flag <0 )
{
perror("gethostname error") ;
return -1 ;
}
printf( "hostname = %s\n", hostname) ;
return 0 ;
}
编译 gcc gethostname.c -o gethostname。
执行 ./gethostname,执行结果如下:
hostname = ubuntu
(4)通过主机名或域名获取IP地址
gethostbyname(通过主机名或域名获取网络信息) | |
所需头文件 | #include <netinet/in.h> |
函数说明 | gethostbyname会返回一个hostent结构指针,该结构具体说明如下: struct hostent { char *h_name; /*正式的主机名称*/ char **h_aliases; /*该主机的其他别名*/ int h_addrtype; /*地址类型,通常是AF_INET*/ int h_length; /*地址的长度*/ char **h_addr_list; /*该主机的所有地址*/ }; |
函数原型 | struct hostent *gethostbyname(const char *name) |
函数传入值 | name:域名或主机名 |
函数传出值 | name:主机的名称 |
函数返回值 | 成功: 返回hostent指针 |
失败:NULL,失败原因存于h_error中(注意错误原因不存于error中) | |
附加说明 | 该函数首先在/etc/hosts文件中查找是否有匹配的主机名。如果没有,则根据在域名解析配置文件 |
(5)gethostbyname函数举例
gethostbyname.c源代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
struct hostent *h;
if (argc != 2) { /* error check the command line */
fprintf(stderr,"usage: getip address\n");
return -1;
}
if ((h=gethostbyname(argv[1])) == NULL) { /* get the host info */
herror("gethostbyname");
return -1;
}
printf("Host name : %s\n", h->h_name);
printf("IP Address : %s\n",inet_ntoa(*((struct in_addr *)h->h_addr)));
return 0;
}
编译 gcc gethostbyname.c -o gethostbyname。
执行./gethostbyname www.sina.com,执行结果如下:
Host name : libra.sina.com.cn
IP Address : 202.108.33.83
摘录自《深入浅出Linux工具与编程》