《unix网络编程》(20)名字与地址转换函数

域名系统(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;
};




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值