域名系统(Domain Name System, DNS)主要用于主机名字与IP地址之间的映射。
gethostbyname / gethostbyaddr函数
通过计算机主机名字来查找。如果查找成功,返回的指向hostent结构的指针,该结构包含所查找主机的所有IPv4地址。这个函数局限于返回IPv4地址。
#include <netdb.h>
#include <sys/socket.h>
struct hostent *gethostbyname(const char *name); //函数调用失败,将返回NULL
<span class="Code">struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};</span>
参数:
char *h_name 表示的是主机的规范名。例如www.google.com的规范名其实是www.l.google.com。
char **h_aliases 表示的是主机的别名。www.google.com就是google他自己的别名。有的时候,有的主机可能有好几个别名,这些,其实都是为了易于用户记忆而为自己的网站多取的名字。
int h_addrtype 表示的是主机ip地址的类型,到底是ipv4(AF_INET),还是ipv6(AF_INET6)
int h_length 表示的是主机ip地址的长度
int **h_addr_lisst 表示的是主机的ip地址,注意,这个是以网络字节序存储的。千万不要直接用printf带%s参数来打这个东西,会有问题的哇。所以到真正需要打印出这个IP的话,需要调用inet_ntop()。
gethostbyaddr函数试图由一个二进制的IP地址找到相应的主机名。
#include <netdb.h>
struct hostent *gethostbyaddr(const char *addr, socklen_t len, int family);
getservbyname / getservbyport函数
和主机一样,服务也通常靠名字来认知。如果在程序中通过其名字而不是端口号来指代一个服务,而且从名字到端口号的映射关系保存到一个文件中(通常是/etc/services),那么即使端口发生变化,只需要修改/etc/services文件中的某一行,不必重新编译应用程序。
getservbyname函数用于根据给定名字查找相应服务。
#include <netdb.h>
struct servent *getservbyname( const char *servname, const char *protoname );
struct servent {
<p> <wbr> <wbr> <wbr> <wbr> <wbr>char <wbr> <wbr> <wbr> <wbr> <wbr>*s_name; <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>// <wbr> <wbr> <wbr>服务的正是名字
<wbr> <wbr> <wbr> <wbr> <wbr>char <wbr> <wbr> <wbr> <wbr> <wbr>**s_aliases; <wbr> <wbr> <wbr> <wbr> <wbr>// <wbr> <wbr> <wbr> 别名列表
<wbr> <wbr> <wbr> <wbr> <wbr>int <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>s_port; <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>// <wbr> <wbr> <wbr>服务端口号
<wbr> <wbr> <wbr> <wbr> <wbr>char <wbr> <wbr> <wbr> <wbr> <wbr>*s_proto; <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr> <wbr>// <wbr> <wbr> <wbr>使用的协议
};</wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></wbr></p>
服务名参数servname必须指定。如果同时指定了协议(即protoname参数为非空指针),那么指定的服务必须有匹配的协议。有些因特网服务既用于TCP也用于UDP提供(如DNS)。如果protoname没有指定而servname指定服务支持多个协议,那么返回那个端口号取决于实现。一般来说,支持多个协议的服务旺旺使用相同的TCP端口号和UDP端口号。
servent结构中关心的主要是端口号。
注意:该端口号是以网络字节的顺序返回的,因此把它存放到套接口地质结构时绝对不能调用htons。
getservbyport函数用于根据端口号和可选协议查找相应服务
#include <netdb.h>
struct servent *getservbyport( int port, const char *protoname );
使用gethostbyname和getservbyname的例子
完整代码下载:http://download.csdn.net/download/u013074465/8621329
#include "myheader.h"
int main(int argc, char **argv) {
int sockfd, n;
char recvline[MAXLINE + 1];
struct sockaddr_in servaddr;
struct in_addr **pptr;
struct in_addr *inetaddrp[2];
struct in_addr inetaddr;
struct hostent *hp;
struct servent *sp;
/*
第一个命令行参数是主机名,我们将参数传递给gethostbyname,第二个参数
是服务名,将它作为参数传递给getservbyname。假设使用的是TCP协议,将
它作为getservbyname的第二个参数。如果gethostbyname失败,尝试使用inet_addr
确定其参数是否已是ASCII格式的地址,如是则构造一个由相应的地址构成的单元素列表
*/
if (argc != 3)
err_exit("usage: daytimetcpcli1 <hostname> <service>");
if ((hp = gethostbyname(argv[1])) == NULL) {
if (inet_aton(argv[1], &inetaddr) == 0)
err_exit("hostname error");
else {
inetaddrp[0] = &inetaddr;
inetaddrp[1] = NULL;
pptr = inetaddrp;
}
}
else
pptr = (struct in_addr **)hp->h_addr_list;
if ((sp = getservbyname(argv[2], "tcp")) == NULL)
err_exit("getservbyname error");
/*
对socket和connect的调用放在循环中,该循环为服务器主机的每个地址执行一次,
直到connect成功或IP地址列表试完为止。调用socket后,我们以服务器主机IP地址
和端口装填网际网套接字地址结构。
*/
for (; *pptr != NULL; pptr++) {
sockfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = sp->s_port;
memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr));
static char str[128];
printf("trying %s\n", Inet_ntop(AF_INET, &servaddr.sin_addr, str,
sizeof(str)));
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == 0) {
break;
}
err_exit("connect error");
close(sockfd);
}
//如果循环终止的原因是没有一个connect调用成功,那就终止程序运行
if (*pptr == NULL)
err_exit("unable to connect");
while ((n = Read(sockfd, recvline, MAXLINE)) > 0) {
recvline[n] = 0;
Fputs(recvline, stdout);
}
exit(0);
}
getaddrinfo函数
gethostbyname和gethostbyaddr函数仅仅支持IPv4.
getinfoaddr函数能够处理名字到地址以及服务到端口的这两种转换,返回一个sockaddr结构而不是一个地址列表。该函数时协议无关的,它将协议相关性完全隐藏在这个库函数内部。
#include <netdb.h>
int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res);
struct addrinfo {
int ai_flags;
int ai_family;//AF_INET,AF_INET6或者AF_UNSPEC
int ai_socktype;//SOCK_STREAM or SOCK_DGRAM
int ai_protocol;//0
size_t ai_addrlen;//往下参数在hints中均为0或NULL
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};