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服务。