域名和域名系统
(一)域名
域名(Domain Name) 是互联网上用于识别和定位计算机、服务器或其他网络资源的名称。是人们用来访问网站或其他服务的地址。例如 example.com 是一个域名。
(二)域名系统
域名系统(Domain Name System,DNS) 是一个分布式的数据库系统,它负责对 IP 地址和域名进行相互转换。例如,当你在浏览器中输入 example.com 时,DNS 将这个域名解析成相应的IP地址,从而帮助你的浏览器找到并连接到 example.com 服务器。
通过域名和 IP 地址访问网站,这两者有什么区别呢?
从结果上看二者没有区别,都能访问同一个网站,但其接入过程不同。域名是赋予服务器端的虚拟地址,而非实际地址,所以要将虚拟地址转化为实际地址,这就是 DNS 的作用。浏览器通过默认 DNS 服务器获取该域名对应的 IP 地址信息,之后才真正接入该网站。
(三)DNS 服务器解析域名 IP 地址的应答过程
计算机内置的默认 DNS 服务器并不知道网络上所有域名的 IP 地址信息。若该 DNS 服务器无法解析,则会询问其他 DNS 服务器,并提供给用户:
IP 地址和域名之间的转换
一般情况下,IP 地址比域名发生变更的概率要高,所以利用 IP 地址编写程序并非上策。一旦注册域名可能永久不变,因此利用域名编写程序会好一些。
(一)利用域名获取 IP 地址
(1)gethostbyname 函数
#include <netdb.h>
// 成功时返回 hostent 结构体地址,失败时返回 NULL 指针
struct hostent* gethostbyname(const char* hostname);
向此函数传递域名字符串,就会返回域名对应的 IP 地址。返回的地址信息装入 hostent 结构体,此结构体定义如下:
struct hostent
{
char *h_name; // 主机的正式名称
char **h_aliases; // 主机的别名列表
int h_addrtype; // 地址类型,通常是 AF_INET(IPv4)或 AF_INET6(IPv6)
int h_length; // 地址长度(以字节为单位)
char **h_addr_list; // 主机的地址列表(IPv4 地址)
};
-
h_name:主机的正式名称。例如,"www.example.com"。 -
h_aliases:主机的别名数组,包含主机的其他名称。这是一个以NULL结尾的字符串数组。 -
h_addrtype:地址类型,例如AF_INET表示 IPv4,AF_INET6表示 IPv6。 -
h_length:主机地址的长度(字节数),对于 IPv4 是 4 字节,对于 IPv6 是 16 字节。 -
h_addr_list:主机地址列表,包含指向主机地址的指针数组。每个地址指针是一个以网络字节序表示的 IP 地址。这个数组也是以NULL结尾的。
(2)示例
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(const char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
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->h_name); // 官方域名
for(i = 0; host->h_aliases[i]; i++)
printf("Aliases %d: %s \n",i + 1, host->h_aliases[i]); //其他域名
printf("Adress type: %s \n", (host->h_addrtype == AF_INET) ? "AF_INET" : "AF_INET6");
for(i = 0; host->h_addr_list[i]; i++) // 获取所有IP
printf("IP addr %d: %s \n", i + 1, inet_ntoa(*(struct in_addr*)host->h_addr_list[i]));
return 0;
}
(3)说明
示例中第 34 行使用了 inet_ntoa 函数进行类型转换,此函数将网络字节序整数型 IP 地址转换成字符串形式,进行转换的原因是 h_addr_list 指向主机地址的指针数组。每个地址指针是一个以网络字节序表示的 IP 地址:
(4)基于 Windows 的实现
Windows 中有类似功能的同名函数:
#include <winsock2.h>
struct hostent* gethostbyname(const char* name);
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
void ErrorHandling(char *message);
int main(int argc, char *argv[])
{
WSADATA wsaData;
int i;
struct hostent *host;
if(argc!=2) {
printf("Usage : %s <addr>\n", argv[0]);
exit(1);
}
if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
ErrorHandling("WSAStartup() error!");
host=gethostbyname(argv[1]);
if(!host)
ErrorHandling("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]));
WSACleanup();
return 0;
}
void ErrorHandling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
(二)利用 IP 地址获取域名
(1)gethostbyaddr 函数
#include <netdb.h>
// 成功时返回 hostent 结构体变量地址值,失败时返回 NULL 指针
struct hostent* gethostbyaddr(const char* addr, socklen_t len, int family);
-
addr: 指向一个包含网络字节序 IP 地址的缓冲区的指针。这个缓冲区应该包含实际的地址数据。 -
len: 地址的长度。对于 IPv4 地址,长度通常是 4 字节;对于 IPv6 地址,长度通常是 16 字节。 -
family: 地址的地址族,通常是AF_INET(IPv4)或AF_INET6(IPv6)。
(2)示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
void error_handling(const char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
int main(int argc, char* argv[])
{
struct hostent* host;
struct sockaddr_in addr;
if(argc != 2)
{
printf("Usage: %s <IP> \n", argv[0]);
exit(1);
}
memset(&addr, 0, sizeof(addrgcc));
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(int 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(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;
}
(3)基于 Windows 的实现
#include <winsock2.h>
struct hostent* gethostbyaddr(const char* addr, int len, int type);
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
void ErrorHandling(char *message);
int main(int argc, char *argv[])
{
WSADATA wsaData;
int i;
struct hostent *host;
SOCKADDR_IN addr;
if(argc!=2) {
printf("Usage : %s <IP>\n", argv[0]);
exit(1);
}
if(WSAStartup(MAKEWORD(2, 2), &wsaData)!=0)
ErrorHandling("WSAStartup() error!");
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)
ErrorHandling("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]));
WSACleanup();
return 0;
}
void ErrorHandling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}