文档章节

网络编程学习——名字与地址转换(一)

thanatos_y
 thanatos_y
发布于 2016/04/16 17:29
字数 2234
阅读 127
收藏 0

#程序员薪资揭榜#你做程序员几年了?月薪多少?发量还在么?>>>

1 域名系统

  域名系统(Domain Name System,DNS)主要用于主机名字与IP地址之间的映射。主机名既可以是一个简单名字(simple name),例如mimiasd,也可以是一个全限定域名(Fully Qualified Domain Name,FQDN),例如mimiasd.unpbook.com。

2 gethostbyname函数

  查找主机名最基本的函数是gethostbyname。如果成功,它就返回一个指向hostent结构的指针,该结构中含有所查找主机的所有IPv4地址。这个函数只能处理IPv4的地址。gethostbyname2函数通过设置AF值,可查看IPv6的地址。

#inlcude <netdb.h>
/* Return entry from host data base for host with NAME.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern struct hostent *gethostbyname (const char *__name);

#ifdef __USE_MISC
/* Return entry from host data base for host with NAME.  AF must be
   set to the address type which is `AF_INET' for IPv4 or `AF_INET6'
   for IPv6.

   This function is not part of POSIX and therefore no official
   cancellation point.  But due to similarity with an POSIX interface
   or due to the implementation it is a cancellation point and
   therefore not marked with __THROW.  */
extern struct hostent *gethostbyname2 (const char *__name, int __af);

  本函数返回的非空指针指向如下的hostent结构。

/* Description of data base entry for a single host.  */
struct hostent
{
  char *h_name;            /* Official name of host.  */
  char **h_aliases;        /* Alias list.  */
  int h_addrtype;        /* Host address type.  */
  int h_length;            /* Length of address.  */
  char **h_addr_list;        /* List of addresses from name server.  */
#if defined __USE_MISC || defined __USE_GNU
# define    h_addr    h_addr_list[0] /* Address, for backward compatibility.*/
#endif
};

 

图1-1 hostent结构和它所包含的信息

  gethostbyname与我们介绍的其他套接字函数的不同之处在于:当错误发生时,它不设置errno变量,而是将全局整数变量h_errno设置为在头文件<netdb.h>中定义的下列常值之一:

/* Possible values left in `h_errno'.  */
# define HOST_NOT_FOUND    1    /* Authoritative Answer Host not found.  */
# define TRY_AGAIN    2    /* Non-Authoritative Host not found,
                   or SERVERFAIL.  */
# define NO_RECOVERY    3    /* Non recoverable errors, FORMERR, REFUSED,
                   NOTIMP.  */
# define NO_DATA    4    /* Valid name, no data record of requested
                   type.  */

  例子:

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

int main( int argc, char **argv )
{
  char *ptr, **pptr;
  char str[ INET_ADDRSTRLEN ];
  struct hostent *hptr;
  
  // 给每个命令行参数调用gethostname
  while( --argc > 0 )
  {
    // 输出规范主机名
    ptr = *++argv;
    if( ( hptr = gethostbyname( ptr ) ) == NULL )
    {
      printf( " gethostbyname error for host: %s: %s ", ptr, hstrerror( h_errno ) );
      continue;
    }
    printf( " official hostname: %s\n ", hptr->h_name );
    
    // 输出别名列表
    for( pptr = hptr->h_aliases; *pptr != NULL; pptr++ )
      printf( " \talias: %s\n ", *pptr );
      
    // pptr指向一个指针数组,其中每个指针指向一个地址。对于每一个地址,我们调用inet_ntop并输出返回的字符串
    switch( hptr->h_addrtype )
    {
      case AF_INET:
        pptr = hptr->h_addr_list;
        for( ; *pptr != NULL; pptr++ )
          printf( " \taddress: %s\n ", inet_ntop( hptr->h_addrtype, *pptr, str, sizeof( str ) ) );
        break;
      
       default:
         printf( " unknown address type " );
         break;
    }
  }
  exit( 0 );
}

 

3 gethostbyaddr函数

  gethostbyaddr函数试图由一个二进制的IP地址找到相应的主机名,与gethostbyname的行为正好相反。

#inlcude <netdb.h>
/* Return entry from host data base which address match ADDR with
   length LEN and type TYPE.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern struct hostent *gethostbyaddr (const void *__addr, __socklen_t __len,
                      int __type);

 

4 getservbyname函数和getservbyport函数

  像主机一样,服务也通常靠名字来认识。如果沃恩在程序代码中通过其名字而不是其端口号来指代一个服务,而且从名字到端口号的映射关系保存在一个文件中(通常是/etc/services),那么即使端口号发生变动,我们需要修改的仅仅是/etc/services文件中的某一行,而不是重新编译应用程序。getservbyname函数用于根据给定查找应用服务。

#inlcude <netdb.h>
/* Return entry from network data base for network with NAME and

   protocol PROTO.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern struct servent *getservbyname (const char *__name, const char *__proto);

  本函数返回的非空指针指向如下的servent结构。

/* Description of data base entry for a single service.  */
struct servent
{
  char *s_name;            /* Official service name.  */
  char **s_aliases;        /* Alias list.  */
  int s_port;            /* Port number.  */
  char *s_proto;        /* Protocol to use.  */
};

  本函数的典型调用如下:

struct servent *sptr;

sptr = getservbyname( " domain ", " udp " ); // DNS using UDP
sptr = getservbyname( " ftp ", " tcp " ); // FTP using TCP
sptr = getservbyname( " ftp ", NULL ); // FTP using TCP
sptr = getservbyname( " ftp ", " udp " ); // this call will fail

  下一个函数getservbyport用于根据给定端口号和可选协议查找相应服务。

#inlcude <netdb.h>
/* Return entry from service data base which matches port PORT and
   protocol PROTO.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern struct servent *getservbyport (int __port, const char *__proto);

  port函数的值必须为网络字节序。本函数的典型调用如下;

struct servent *sptr;

sptr = getservbyport( hton( 53 ), " udp " ); // DNS using UDP
sptr = getservbyport( hton( 21 ), " tcp " ); // FTP using TCP
sptr = getservbyport( hton( 21 ), NULL ); // FTP using TCP
sptr = getservbyport( hton( 21 ), " udp " ); // this call will fail

  必须清楚的是,有些端口号在TCP上用于一种服务,在UDP上却用于完全不同的另一种服务。

  例子:使用gethostbyname和getservbyname

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define MAX_MESG_SIZE 1024

char *  sock_ntop(const struct sockaddr *sa, socklen_t salen);
char *  Sock_ntop(const struct sockaddr *sa, socklen_t salen);

int main( int argc, char **argv )
{
  int sockfd, n;
  char recvline[ MAX_MESG_SIZE + 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_aton函数,确定其参数是否已是ASCII格式的地址,若是则构造
  // 一个由相应的地址构成的单元素列表。
  if( argc != 3 )
  {
    printf( " usage:daytimecpcli1 <hostname> <service> \n" );
    exit( 1 );
  }
  
  if( ( hp = gethostbyname( argv[ 1 ] ) ) == NULL )
  { 
    if( inet_aton( argv[ 1 ], &inetaddr ) == 0 )
    {
      printf( " hostname error for %s: %s \n", argv[ 1 ], hstrerror( h_errno ) );
      exit( 1 );
    } 
    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 )
  {
    printf( " getservbyname error for %s \n", argv[ 2 ] );
    exit( 1 );
  }
  
  // 我们把对socket和connect的调用放在一个循环中,该循环为服务器主机的每个地址执行一次,直到
  // connect成功或IP地址列表试完为止。调用socket以后,我们以服务器主机的IP地址和端口填装网际网套接字
  // 地址结构。尽管我们可以把对bzero的调用和它后面的两个赋值语句置于循环体之外以提高执行效率,不过下面代码要
  // 简介些。与服务器建立连接几乎不会称为网络客户的性能瓶颈。
  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 ) );
    printf( " trying %s\n ", Sock_ntop( ( struct sockaddr* ) &servaddr, sizeof( servaddr ) ) );
    
    //接着调用connect。如果调用成功,那就使用break语句终止循环,否则输出一个出错消息并关闭套接字。我们直到
    // connect调用失败的描述符必须关闭,不能再用。
    if( connect( sockfd, ( struct sockaddr* ) &servaddr, sizeof( servaddr ) ) == 0 )
      break; // success
    else
    {
    printf( " connect error \n" );
    close( sockfd );
    }
  }
  
  if( *pptr == NULL )
  {
    printf( " unable to connect \n" );
    exit( 1 );
  }
  
  while( ( n = read( sockfd, recvline, MAX_MESG_SIZE ) ) > 0 )
  {
    recvline[ n ] = 0; // null terminate
    fputs( recvline,stdout );
  }
  exit( 0 );
}

 

5 getaddrinfo函数

  gethostbyname和gethostbyaddr这两个函数仅仅支持IPv4。getadddrinfo函数能够处理名字到地址以及服务到端口这两种转换,返回的是一个sockaddr结构而不是一个地址列表。

#include <netdb.h>
/* Translate name of a service location and/or a service name to set of
   socket addresses.

   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern int getaddrinfo (const char *__restrict __name,
            const char *__restrict __service,
            const struct addrinfo *__restrict __req,
            struct addrinfo **__restrict __pai);

  本函数通过__pai指针参数返回一个指向addrinfo结构链表的指针,而adddrinfo结构定义在头文件<netdb.h>中。

/* Structure to contain information about address of a service provider.  */
struct addrinfo
{
  int ai_flags;            /* Input flags.  */
  int ai_family;        /* Protocol family for socket.  */
  int ai_socktype;        /* Socket type.  */
  int ai_protocol;        /* Protocol for socket.  */
  socklen_t ai_addrlen;        /* Length of socket address.  */
  struct sockaddr *ai_addr;    /* Socket address for socket.  */
  char *ai_canonname;        /* Canonical name for service location.  */
  struct addrinfo *ai_next;    /* Pointer to next in list.  */
};

  在addrinfo结构中返回的信息可现在用于socket调用,随后现成用于适合客户的connect或senfto调用,或者是适合服务器的bind调用。

图1-2 getaddrinfo返回信息的实例

  端口53用于domain服务。这个端口在套接字地址结构中按照网络字节序存放。返回的ai_protocol值或为IPPROTO_TCP,或为IPPROTO_UDP。

图1-3 为每个IP地址返回的addrinfo结构的数目

 

6 gai_strerror函数

  图1-4给出了可由getaddrinfo返回的非0错误值的名字和含义。gai_strerror以这些值为它的唯一参数,返回一个指向对应的出错信息串指针。

/* Convert error return from getaddrinfo() to a string.  */
extern const char *gai_strerror (int __ecode) __THROW;

图1-4 getaddrinfo返回的非0错误常值

7 freeaddrinfo函数

  由getaddrinfo返回的所有存储空间都使动态获取的(譬如来自malloc调用),包括addrinfo结构、ai_addr结构和ai_cannonname字符串。这些诶储存空间通过调用freeaddrinfo返还给系统。

#include <netdb.h>
/* Free `addrinfo' structure AI including associated storage.  */
extern void freeaddrinfo (struct addrinfo *__ai) __THROW;

  __ai参数指向由getaddrinfo返回的第一个adddinfo结构。这个链表中的所有结构以及由它们指向的任何动态存储空间都被释放掉。

 

© 著作权归作者所有

thanatos_y
粉丝 9
博文 112
码字总数 315059
作品 0
成都
程序员
私信 提问
加载中

评论(0)

网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?

本文引用了“帅地”发表于公众号苦逼的码农的技术分享。 1、引言 搞网络通信应用开发的程序员,可能会经常听到外网IP(即互联网IP地址)和内网IP(即局域网IP地址),但他们的区别是什么?又...

首席大胸器
2018/11/20
235
0
IP地址详解-你,是谁?

一、简介 在生活中我们使用具有上网功能的电子设备都有IP地址,就跟每个人都有自己的名字一样。IP地址分为IPV4 IPV6,我们所说的的IP地址指的是IPV4的地址。 只要记住你的名字 不管你在世界的...

叶焕新
2017/03/28
0
0
脑残式网络编程入门(六):什么是公网IP和内网IP?NAT转换又是什么鬼?

本文引用了“帅地”发表于公众号苦逼的码农的技术分享。 1、引言 搞网络通信应用开发的程序员,可能会经常听到外网IP(即互联网IP地址)和内网IP(即局域网IP地址),但他们的区别是什么?又...

JackJiang2011
2018/11/20
0
0
UNIX SOCKET编程简介

1 . Layered Model of Networking Socket 编程的层次模型如下图所示, 最上面是应用层,应用层下面的是 SOCKET API 层,再下面是传输层和网络层…… 实际上, Sockets API 层并不是一个真正...

osc_o3pzw5ip
2018/01/03
5
0
Linux高级网络编程系列教程【转】

(转自:https://blog.csdn.net/lianghework/article/details/45190463) 一、网络应用层编程 二、网络底层编程(黑客模式)

huangzj0708
03/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

1M带宽服务器并发数可支撑多少人同时在线?

服务器1M公网带宽能同时承受多少人同时在线?很多云厂商如阿里云、腾讯云推出的很多服务器活动默认配置1M带宽,很多站长认为服务器1M带宽小水管,事实上服务器1M带宽支撑日均2000IP的网站是够...

码笔记
昨天
18
0
C#简单入门——适合初学入门

一、第一个C#程序 using System;namespace HelloWorldApplication // 命名空间声明{ /* 类名为 HelloWorld */ class HelloWorld // 一个 class { /* main函数 ...

ittzg
昨天
9
0
DDD之2领域概念

图中是暗黑领域,非常牛逼的技能。 背景 DDD中出现的名词: 领域,子领域,核心域,通用域,支撑域,限界上下文,聚合,聚合根,实体,值对象 都是关键概念,但是又比较晦涩,在开始DDD之前,...

李福春carter
昨天
12
0
Vue基础学习备忘

内置指令 v-bind v-model v-if/v-else/v-show v-for v-on v-text v-html v-el v-ref v-pre v-cloak v-once 过滤器 作用是对数据再次加工,例如:后台返回时间戳,vue通过过滤器进行数据格式化...

Apache软件基金会主席
昨天
26
0
使用Git版本控制查看文件的更改历史记录 - View the change history of a file using Git versioning

问题: How can I view the change history of an individual file in Git, complete details with what has changed? 如何在Git中查看单个文件的更改历史记录,完整的详细信息? I have got......

技术盛宴
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部