介绍一些linux套接字编程中常见的地址转换等相关函数。
地址示例:
主机名(域名):www.yuming.com
IP地址:192.168.15.1
服务名:www
端口号: 80
1、gethostbyname, gethostbyaddr
一般来说,网络中使用的地址都是域名地址,这样更加直观可读,需要将地址进行相应的转换来获取实际需要的地址形式。
/* gethostbyname
* 返回值:成功则为非NULL,错误则为NULL且设置h_errno;
* 定义:
*/
#include
struct hostent *gethostbyname(const char *hostname);
struct hostent{
char *h_name; /* official name of host */
char **h_aliases; /* pointer to array of pointers to alias names */
int h-addrtype; /* host address tyep: AF_INET; */
int h_length; /* length of address :4 */
char **h_addr_list; /*ptr to array of ptrs with ipv4 addrs */
};
/* 说明:
* 函数执行A记录查询,只能返回IPv4地址;
* 函数的返回结构包含有以下信息:
* 1、主机官方名;
* 2、主机别名,可以有多个;
* 3、地址家族类型;
* 4、地址长度(IP);
* 5、IP地址,这是个指针,可以有多个;
*/
/* gethostbyaddr
* 返回值:成功则为非NULL,错误则为NULL且设置h_errno;
* 定义:
*/
#include
struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);
/* 说明:
* 参数addr不是字符串指针,而是IPv4地址中某个in_addr结构指针;
* len为addr的长度,family为AF_INET;
* 函数就是通过套接字中的二进制IP地址来进行主机地址查询;
*/
2、getservbyname,getservbyport
对于服务(http,ftp,domain)来说,也是经常用名字来认知的,但是服务一般都是对应端口的,有时需要在服务名和端口间进行转换等操作,这里需要相应的函数来返回对应于给定服务名和协议名的相关服务信息(服务和端口都是固定分配定义好的,如端口80用于网页服务, 开放端口除外)。
/* getservbyname
* 返回值:成功则为非空指针,失败则为NULL;
* 定义:
*/
#include
struct servent *getservbyname(const char *servname, const char *protoname);
struct servent{
char *s_name; /* official service name */
char **s_aliases; /* alias list */
int s_port; /* port number, network by order */
char *s_proto; /* protocol to use */
};
/* 说明:
* servname为服务名称,protoname为协议名称(tcp,udp);
* 服务器和协议名要保持正确,不能给要给服务不支持的协议;
* 返回的服务结构类型包含数据:
* 1、官方服务名;
* 2、服务别名列表;
* 3、服务端口号,网络字节序;
* 4、使用的协议;
*/
/* getservbyport
* 返回值:成功则为非空指针,失败则为NULL;
* 定义:
*/
#include
struct servent *getservbyport(int port, const char *protoname);
/* 说明:
* 和函数getservbyname作用相同,区别在于这里利用的是端口号进行查询;
* port必须为网络字节序,即若查询端口80则参数为htons(80),服务名返回为www;
*/
3、getaddrinfo
函数能够进行名字到地址和服务到端口的转换,前面的几个函数只能处理IPv4,这个函数可以处理IPv4和IPv6,返回的是一个sockaddr结构,可以直接用于套接字函数。
/*
* 返回值:成功则为0,出错则非0;
* 定义:
*/
#include
int getaddrinfo(const char *hostname, const char *service,
const struct addrinfo *hints, struct addrinfo **result);
/* 说明:
* hostname为一个主机名或点分十进制地址串,service为服务名或十进制端口号;
* hints可以是一个空指针,也可以是一个结构指针,用来进行输出设置;
* result为返回信息,若是有多个地址,则会有多个结构指针返回;
*/
struct addrinfo{
int ai_flags; /* 标志位 */
int ai_family; /* 家族类型,AF_? */
int ai_socktype; /* 套接字类型 SOCK_? */
int ai_protocol; /* 协议类型,IPPROTO_ */
socklen_t ai_addrlen; /*结构ai_addr 的长度 */
char *ai_canonname; /* 主机权威名 */
struct sockaddr *ai_addr; /* 套接字结构指针 */
struct addrinfo *ai_next; /* 下一个结构指针 */
};
/* 说明:
* hints结构可以设置前面4个int型数据;设空指针则ai_family为AF_UNSPEC,其他为0;
* 具体细节参数需了解ai_flags标志;
* 若有多个地址会返回多个结构,通过ai_next可以获取下一个结构指针;
*/
参考:getaddrinfo函数详解; UNIX套接字编程卷1;
4、freeaddrinfo
/*
* 函数功能:释放内存;
* 定义:
*/
#include
void freeaddrinfo(struct addrinfo *ai);
/* 说明:
* 调用getaddrinfo函数会返回的所有空间都是动态获取的;
* 通过调用函数freeaddinfo来释放动态存储空间;
*/
5、getnameinfo
getnameinfo函数功能与getaddrinfo函数功能相反,它通过传入一个套接字结构数据,返回主机的地址的服务名称,函数为协议无关,无需关心在套接字地址结构中的协议地址的类型。
/*
* 函数功能:返回套接字中描述主机和服务的字符串;
* 返回值:成功则为0,出错则非0;
* 函数定义
*/
#include
int getnameinfo(const struct sockaddr *sockaddr, socklen_t addrlen,
char *host, socklen_t hostlen,
char *serv, socklen_t servlen, int falgs);
/* 说明:
* 前面两个参数为输入参数,一个为套接字结构指针,一个为该套接字长度;
* host指向输出主机字符串,hostlen为其长度,不想输出host,则hostlen可以置为0;
* serv执行输出服务字符串,servlen为其长度,不限输出serv,则servlen可以置为0;
* flags用于改变函数操作,有下面的取值:
* 1、NI_DGRAM:只用于处理数据报;
* 2、NI_NAMEREQD:不能解析出名字则返回错误;
* 3、NI_NOFQDN:返回的主机名第一个点号之后的部分被截掉;
* 4、NI_NUMERICHOST:不使用DNS,以数值表达格式以字符串形式返回IP地址;
* 5、NI_NUMERICSERV:以十进制数以字符串形式返回服务名(端口号);
* 各标志可以进行组合;
总结:
一个网络主机的与另一个主机进行数据交换需要双方的网络地址,包括IP地址和端口号。一般来说,为了简便使用,我们使用DNS(域名系统)定义一个名字来标识一个IP地址,用服务名来标识一个端口号。有时我们需要进行这两种表达方式之间的转换,这就需要上面介绍的一些函数了。
gethostbyname和gethostbyaddr就是用于主机名称的转换函数,前者通过域名可以获取IP地址,后者可以通过IP获取域名,两者返回的值类型一样,需要的数据都可以在结构hostent中找到。getservbyname和getservbyport则是针对端口号的转换函数。
gethostbyname和gethostbyaddr函数只能应用于IPv4,有一定的局限性。getaddrinfo和getnameinfo函数解决了这个问题,可以同时处理IPv4和IPv6。