域名系统
这里对计算机网络小白简要的介绍一下域名系统,像我们平时上网用的都是类似于www.baidu.com的域名。就是说我们可以通过IP地址和域名同时访问一个网站,那么两则到底有何区别?其实可以这么理解,域名是IP地址的虚拟地址,而非实际地址。因此需要将虚拟地址转换成实际地址。这个转换的重任就是交给DNS服务器。
每台计算机中都有一台默认DNS服务器,如果计算机想要查询某一个域名对应的IP地址,它首先就会向默认DNS服务器发送查询请求,如果这台DNS服务器并不知道某一个域名对应的IP地址,它会向上一级DNS服务器查询。到达顶级DNS服务器后它肯定知道向哪个DNS发出请求。进而向下级DNS传递解析请求,得到IP地址后原路递归回去。最终计算机可以知道目标域名的IP地址。
程序中有必要使用域名吗?
这个问题回答了我们学习本章的原因。试想一个情景,我们在为一个公司开发一个客户端使用公司提供的服务。那么如果要使用公司的服务,那么肯定要访问公司的服务器。那么保存在客户端内部的是服务器域名还是服务器地址呢?系统运行时,保持IP地址并不容易,系统相关的各种原因会导致IP地址变更。那么由于变更,我们需要时时刻刻修改源代码中的IP地址,这对于一个不具有专业知识的使用者是不现实的。
一旦注册域名可能永久不变,因此利用域名编写程序会好一些。这样运行程序时根据域名获得IP地址,再接入服务器,这样程序就不会依赖于服务器IP地址了。所以说,程序中也需要IP地址和域名之间的转换函数。
利用域名获取IP地址
通过以下函数可以通过传递字符串格式的域名来获取IP地址。
#include<netdb.h>
struct hostnet * gethostbyname(const char*hostname);
//成功时返回houstnet结构体地址,失败时返回NULL指针
先介绍以下此结构体定义:
struct hostent{
char * h_name; //official name
char ** h_aliases; //alias list
int h_addrtype; //host address type
int h_length; //address length
char ** h_addr _list; //address list
}
h_name
该变量中存有官方域名(Official domain name)。官方域名代表某一主页,但实际上,一些
著名公司的域名并未用官方域名注册。
h_aliases
可以通过多个域名访问同一主页。同一IP可以绑定多个域名,因此,除官方域名外还可指定
其他域名。这些信息可以通过h_aliases获得。
h_addrtype
gethostbyname函数不仅支持IPv4,还支持IPv6。因此可以通过此变量获取保存在h_addr_list的IP地址的地址族信息。若是IPv4,则此变量存有AF_INET。
h_length
保存IP地址长度。若是IPv4地址,因为是4个字节,则保存4;IPv6时,因为是16个字节,故保存16。
h_addr_list
这是最重要的成员。通过此变量以整数形式保存域名对应的IP地址。另外,用户较多的网站有可能分配多个IP给同一域名,利用多个服务器进行负载均衡。此时同样可以通过此变量获取地址信息。
下面通过一个示例来进行演示:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<arpa/inet.h>
#include<netdb.h>
void error_handling(char* message);
int main(int argc,char*argv[]){
int i;
struct hostent *host;
if(argc!=2){
printf("Usage : %s <addr>\n",argv[0]);
exit(1);
}
host=gethostbyname(argv[1]);
if(!host)error_handling("gethost...error");
printf("Official name: %s \n",host->name);
for(int i=0;host->h_aliases[i];i++)printf("Aliases %d: %s\n",i+1,host->aliases[i]);
printf("Address type: %s\n",(host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6");
for(int i=0;host->h_addr_list[i];i++)
printf("IP addr %d: %s \n",i+1,inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
return 0;
}
voie error_handling(char *message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
现在讨论上述示例的第26-28行。如果只看hostent结构体的定义,结构体成员h_addr_list指向字符串指针数组。但时字符串指针数组中的元素实际指向的时in_addr结构体变量的地址值而非字符串。
利用IP地址获取域名
#include<netdb.h>
struct hostent *gethostbyaddr(const char* addr,socklen_t len,int family);
//成功时返回hostent结构体变量地址,失败时返回NULL指针
addr //含有IP地址信息的in_addr结构体指针。为了同时传递IPv4地址以外的其他信息,该变量声明为char指针
len //第一个参数传递的地址信息的字节数
family //传递的地址族信息
下面通过示例来演示该函数的使用方法:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(char *message);
int main(int argc, char *argv[]){
int i;
struct hostent *host;
struct sockaddr_in addr;
if(argc!=2){
printf("Usage : %s <IP>\n", argv[0]);
exit(1);
}
memset(&addr, 0, sizeof(addr));
addr.sin_addr.s_addr=inet_addr(argv[1]);
host=gethostbyaddr((char*)&addr.sin_addr,4, AF_INET);
if(!host)error handling("gethost... error");
printf("Official name: %s \n", host->h_name);
for(i=0; host->h_aliases[i]; i++)printf("Aliases %d: %s \n", i+1, host->h_aliases[i]);
printf("Address type: %s \n",(host->h_addrtype==AF_INET)?"AF_INET":"AF_INET6");
for(i=0; host->h_addr_list[i]; i++)
printf("IP addr %d: %s \n",i+1,inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
return 0;
}
voie error_handling(char*message){
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
基于Windows的实现
Windows平台中也有类似功能的同名函数。先介绍gethostbyname函数.
#include<winsock2.h>
struct hostent* gethostbyname(const char *name);
//成功返回hostent结构体变量地址值,失败时返回NULL指针
函数名,参数及返回类型与Linux中没有区别,介绍下一函数
#include<winsock2.h>
struct hostent* gethostbyaddr(const char* addr,int len,int family);
//成功时返回hostent结构体变量地址,失败时返回NULL指针
上述函数也于Linux中的函数完全一致。