18.4.1  查询主机名称

查询主机名称是通过访问主机数据库实现的,服务器数据库接口函数在头文件netdb.h中定义。与此相关的函数有sethostbyaddr()和gethostbyname()两个,它们的一般形式如下:

struct hostent *gethostbyaddr(const void *addr, size_t len, int type);
struct hostent *gethostbyname(const char *name);

 

函数的返回值是指向hostent结构的指针,该结构用于保存主机名称等信息,hostent结构的定义如下:

 

struct hostent {
   char *h_name;                     // 主机名
   char **h_aliases;                 // 别名列表
   int h_addrtype;                  // 地址类型
   int h_length;                // 地址的字节长度
   char **h_addr_list                // 地址列表
};

 

gethostbyaddr()是通过IP地址查询主机信息,gethostbyname()是通过主机名查询主机信息。如果在主机数据库中没有查到相关主机或地址的项,这些函数会返回一个空指针。

与服务及其关联的端口号有关的信息可以通过getservbyname()函数和getservbyport()函数查询,它们的一般形式如下:

 

struct servent *getservbyname(const char *name, const char *proto);
struct servent *getservbyport(int port, const char *proto);

其中,proto参数指定了用来连接到该项服务的协议,SOCK_STREAM类型的TCP连接对应的是tcp,UDP连接对应的是udp。函数的返回值是servent结构指针,该结构的定义如下:

struct servent {
   char *s_name;             // 服务名
   char **s_aliases;         // 服务别名列表
   int s_port;              // 端口号
   char *s_proto;            // 协议类型
};

如果需要将地址信息转换为四分十进制法表示,可使用inet_ntoa()函数来完成。该函数被包含在头文件“arpa/inet.h”中,它的一般形式是:

 

char *inet_ntoa(struct in_addr in);

 

如果执行成功,它将返回一个指向四分十进制法表示地址的字符串的指针,否则返回–1。查询当前主机的主机名的函数是gethostname(),该函数的一般形式是:

int gethostname(char *name, int namelength);

 

如果执行成功,*name参数所指向的内存空间将被写入主机名,namelength参数限定了*name参数所指向内存空间的长度。如果主机名太长,会被截短到namelength限定的长度。函数执行成功时返回0,否则返回–1。下面用一个示例说明查询主机名称操作的方法:

#include <sys/socket.h>                     // 包含套接字相关函数
#include <netinet/in.h>                     // 包含AF_INET相关结构
#include <netdb.h>                          // 包含读取主机信息的相关函数
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
   char *host;                               // 用于保存主机名
   int sockfd;                              // 用于保存套接字标识符
   int len, result;
   struct sockaddr_in address;              // 定义套接字地址
   struct hostent *hostinfo;             // 定义主机信息结构
   struct servent *servinfo;             // 定义服务信息结构
   char buffer[128];
   if (argc == 1)
      host = "localhost";                    // 如果没有指定主机名,则置为本机
   else
      host = argv[1];
   hostinfo = gethostbyname(host);          // 获得主机信息
   if (!hostinfo) {
      fprintf(stderr, "找不到主机: %s/n", host);
      return 1;
   }
   servinfo = getservbyname("daytime", "tcp");      // 获得服务信息
   if (!servinfo) {
      fprintf(stderr, "无daytime服务/n");
      return 1;
   }
   printf("daytime服务端口是:%d/n", ntohs(servinfo -> s_port));  
                                                    // 输出端口信息
   sockfd = socket(AF_INET, SOCK_STREAM, 0);    // 建立套接字
   address.sin_family = AF_INET;                // 定义套接字地址中的域
   address.sin_port = servinfo -> s_port;           // 定义套接字端口
   address.sin_addr = *(struct in_addr *) *hostinfo -> h_addr_list;
                                                    // 定义套接字地址   len = sizeof(address);
   result = connect(sockfd, (struct sockaddr *) &address, len);
                                                    // 请求连接
   if (result == -1) {
      perror("获得数据出错");
      return 1;
   }
   result = read(sockfd, buffer, sizeof(buffer));   // 接收数据
   buffer[result] = '/0';
   printf("读取%d字节:%s", result, buffer);         // 输出数据
   close(sockfd);                                   // 关闭连接
   return 0;
}

运行程序时,将一个UNIX服务器地址作为该程序的运行参数。daytime服务的端口号是通过网络数据库函数getserverbyname()确定的,这个函数返回的是关于网络服务方面的资料,它们和主机资料差不多。程序会先尝试连接指定主机信息数据库里的地址,如果成功就读取daytime服务返回的信息,该信息是一个表示UNIX时间和日期的字符串。如果测试平台是Linux桌面操作系统,修改“/etc/xinetd.d/daytime”文件,将此文件中两个disable的值由yes改为no,再重启计算机即可运行daytime服务。