在套接字编程中,我们经常使用数字的 IP 地址和端口号进程编程,但是我们平常所熟悉的是一些便于记忆的字符名字,要使这种名字能够为套接字操作函数识别,所以这两者之间必须存在着某种转换关系。本节介绍的是 【主机名 与 地址】 和 【服务名 与 端口号】 之间的转换。在 Unix 系统中,可以使用函数 gethostbyname、gethostbyaddr 实现【主机名 与 地址】之间的转换;可以使用函数 getservbyname、getservbyport 实现 【服务名 与 端口号】 之间的转换。但是前面这些函数只适合在 IPv4 域里面,若要在 IPv4 和 IPv6 实现这些功能,则可以使用 getaddrinfo 函数。
gethostbyname 与 gethostbyaddr 函数
/* 主机名与地址之间转换 */
/*
* 函数功能:主机名与地址之间转换;
* 返回值:若成功则返回主机结构指针,若出错则返回NULL;
* 函数原型:
*/
#include <netdb.h>
struct hostent *gethostbyname(const char *hostname);//将主机名转换为数字地址;
struct hostent *gethostaddr(const char *addr, size_t len, int family);//将数字地址转换为主机名;
/* 函数功能:获取主机信息;
* 函数原型:
*/
struct hostent *gethostent(void);/* 获取主机信息,并返回hostent结构指针 */
void sethostent(int stayopen);/* 设置主机信息 */
void endhostent(void);
/*
* 说明:
* 若主机数据文件没有打开,gethostent会打开它,该函数返回文件的下一条目;
* 函数sethostent会打开文件,若文件已打开,那么将其回绕;
* 函数endhostent将关闭文件;
* 其中hostent结构至少包含如下成员数据:
*/
struct hostent
{
char *h_name; /* official name of host */
char **h_aliases; /* pointer to alternate host name array */
int h_addrtype; /* address type: AF_INET */
int h_length; /* length in bytes of address: 4 */
char **h_addr_list; /* pointer to array of IPv4 address */
};
getservbyname 与 getservbyport 函数
/* 服务名与端口号之间的转换 */
/*
* 函数功能:服务名与端口号之间的转换;
* 返回值:若成功则返回指针,若出错则返回NULL;
* 函数原型:
*/
#include <netdb.h>
struct servent *getservbyname(const char *servname, const char *protoname);
struct servent *getservbyport(int port, const char *protoname);
struct servent *getservent(void);
void setservent(int stayopen);
void endservent(void);
/*
* protoname参数若为空,则返回取决与实现,若为非空,则指定协议名称;
*
* 其中servent 结构至少包含以下成员:
*/
struct servent
{
char *s_name; /* official service name */
char **s_aliases; /* pointer to alternate service name array */
int s_port; /* port number */
char *s_proto; /* name of protocol */
};
#include "unp.h"
int
main(int argc, char **argv)
{
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
struct in_addr **pptr = NULL;
struct in_addr *inetaddrp[2];
struct in_addr inetaddr;
struct hostent *hp;
struct servent *sp;
if (argc != 3)
err_quit("usage: %s <hostname> <service>", argv[0]);
/* 将主机名作为gethostbyname的参数,并获取hostent结构信息 */
if ( (hp = gethostbyname(argv[1])) == NULL) {
/* 若gethostbyname获取失败,则使用inet_aton构造地址单元素列表 */
if (inet_aton(argv[1], &inetaddr) == 0) {
err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno));
} else {
inetaddrp[0] = &inetaddr;
inetaddrp[1] = NULL;
pptr = inetaddrp;
}
} else {/* 若gethostbyname成功,则pptr指向地址链表 */
pptr = (struct in_addr **) hp->h_addr_list;
}
/* 将服务名作为getservbyname的参数,获取servent结构信息 */
if ( (sp = getservbyname(argv[2], "tcp")) == NULL)
/* 若失败则退出 */
err_quit("getservbyname error for %s", argv[2]);
/* 遍历地址结构列表中的每一个地址 */
for ( ; *pptr != NULL; pptr++) {
/* 创建基于TCP套接字 */
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)))
err_sys("socket error");
/* 初始化服务器地址信息 */
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = sp->s_port;/* 端口号由getservbyname获取 */
/* 复制服务器地址信息,该地址信息由gethostbyname获取 */
memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
/* 以下对地址列表中的每个服务器地址尝试连接 */
printf("trying %s\n",
Sock_ntop((SA *) &servaddr, sizeof(servaddr)));
if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) == 0)
break; /* success */
/* 若地址连接失败,则关闭与该地址相关的套接字 */
err_ret("connect error");
close(sockfd);
}
/* 检查是否所有服务器地址都连接失败 */
if (*pptr == NULL)
err_quit("unable to connect");
/* 处理函数,读取服务器应答信息,并显示到标准输出 */
while ( (n = Read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0; /* null terminate */
Fputs(recvline, stdout);
}
exit(0);
}
getaddrinfo 函数
/* IPv6、IPv4 都可使用 */
/*
* 函数功能:将 服务名与端口号 和 主机名与地址 之间转换;
* 返回值:若成功则返回0,若出错则返回非0错误编码;
* 函数原型:
*/
#include <netdb.h>
#include <sys/socket.h>
int getaddrinfo(const char *hostname, const char *service, const struct addrinfo *hints, struct addrinfo **result);
void freeaddrinfo(struct addrinfo *ai);/* 把从getaddrinfo函数动态分配的成员结构内存返回给系统,参数ai是由函数getaddrinfo返回的第一个addrinfo结构 */
const char *gai_strerror(int error);//若getaddrinfo出错时,错误消息只能由该函数输出;
/*
* 说明:
* 该函数需要提供主机名或服务名,若只提供其中一个,则另一个必须指定为NULL;
* addrinfo是一个结构链表,其定义如下:
*/
struct addrinfo
{
int ai_flags; /* customize behavior */
int ai_family; /* address family */
int ai_socktype; /* socket type */
int ai_protocol; /* protocol */
socklen_t ai_addrlen; /* length in bytes of address */
struct sockaddr *ai_addr; /* address */
char *ai_canonname; /* canonical name of host */
struct addrinfo *ai_next; /* next in list */
};
/*
* 函数功能:将地址转换成服务名或主机名;
* 返回值:若成功则返回0,若出错则返回非0值;
* 函数原型:
*/
#include <netdb.h>
#include <sys/socket.h>
int getnameinfo(const struct sockadd *addr, socklen_t alen, char * host, socklen_t hostlen,
char * service, socklen_t servlen, unsigned int flags);
/*
* 说明:
* addrinfo结构成员:
* ai_flags 取值如下:
* (1)AI_PASSIVE 套接字将用于被动打开;
* (2)AI_CANONNAME 告知getaddrinfo函数返回主机的规范名字;
* (3)AI_NUMERICHOST 防止任何类型的名字到地址映射,hostname必须是一个地址串;
* (4)AI_NUMERICSERV 防止任何类型的名字到服务映射,service必须是一个十进制端口号数串;
* (5)AI_V4MAPPED 若同时指定ai_family值为AF_INET6,若没有可用的AAAA记录,则返回与A记录对应的IPv4映射的IPv6地址;
* (6)AI_ALL 若同时指定AI_V4MAPPED标志,除了返回与AAAA记录对应的IPv6地址外,还返回与A记录对应的IPv4映射的IPv6地址;
* (7)AI_ADDRCONFIG 按照所在主机的配置选择返回地址类型;
*/
《Unix 网络编程》
原文地址:http://blog.csdn.net/chenhanzhun/article/details/41929869