《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址
《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址
- 《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址
- 域名系统
- 什么是域名?
- DNS 服务器
- IP 地址和域名之间的转换
- 使用域名的必要性
- 利用域名获取 IP 地址
- 利用 IP 地址获取域名
- 基于 Windows 的实现
- Windows 下的 gethostbyname 函数的示例程序
- Windows 下的 gethostbyname 函数的示例程序
- 习题
- (1)下列关于DNS说法错误的是?
- (2)阅读如下对话,并说明东秀的解决方案是否可行。这些都是大家可以在大学计算机实验室验证的内容。
- (3)在浏览器地址栏输入www.orentec.co.kr,并整理出主页显示过程。假设浏览器访问的默认DNS服务器中并没有关于www.orentec.co.kr的IP地址信息。
《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址
域名系统
DNS(Domain Name System,域名系统),是对IP地址和域名进行互相转换的系统,其核心是DNS服务器。
什么是域名?
提供网络服务的服务器端也是通过IP地址区分的,但IP地址难记,因此将容易记、易表述的域名分配并取代IP地址。
DNS 服务器
在浏览器地址栏输入www.baidu.com,或用ping命令获取其IP地址,然后在浏览器地址栏输入IP地址便可访问百度主页,那么通过域名访问和通过IP访问这二者有何区别?
从结果上来看这两者没有区别,都是进去百度网页。实际上,域名是赋予服务器端的虚拟地址,而非实际地址。因此需要将虚拟地址转化为实际地址。
那么,如何将域名转化为IP地址呢?DNS服务器承担此重任,可以向DNS服务器请求转换地址。
所有计算机中都记录着默认DNS服务器地址,就是通过默认DNS服务器得到相应域名的IP地址信息。在浏览器地址栏中输入域名后,浏览器通过默认DNS服务器获取该域名对应的IP地址信息,之后才真正接入该网站。
计算机内置的默认DNS服务器并不知道网络上所有域名的IP地址信息,若该DNS服务器无法解析,则会询问其他DNS服务器并提供给用户,如下图所示:
上图展示了默认DNS服务器无法解析主机询问的域名IP地址时的应答过程,可以看出,默认DNS服务器收到自己无法解析的请求时,向上级DNS服务器询问,通过这种方式逐级向上传递信息,到达顶级DNS服务器时——根DNS服务器,它知道该向哪个DNS服务器询问。向下级DNS传递解析请求,得到IP地址后原路返回,最终将解析的IP地址传递到发起请求的主机,DNS就是这样层次化管理的一种分布式数据库系统。
IP 地址和域名之间的转换
使用域名的必要性
- 用户友好性: 域名通常比IP地址更易于记忆和识别,使用域名可以提高用户体验。
- 可扩展性: 域名允许在不更改程序代码的情况下更换服务器IP地址,这在服务器迁移或扩展时非常有用。
- 负载均衡: 通过域名,可以更容易地实现负载均衡,将请求分发到多个服务器上。
- 安全性: 域名可以与SSL/TLS证书结合使用,提供安全的HTTPS连接,保护数据传输的安全。
- 易于管理: 使用域名可以简化网络配置和维护,因为域名解析和管理通常比IP地址更简单。
利用域名获取 IP 地址
#include<netdb.h>
struct hostent *gethostbyname(const char * hostname);
成功时返回hostent结构体地址,失败时返回NULL指针。
参数:
- hostname:域名字符串。
hostent结构体定义如下:
struct hostent
{
char *hname; // official name
char **h aliases; // alias list
int h_addrtype; // host address type
int h_length; // address length
char **h_addr_list; // address list
}
从上述结构体可以看出,当调用gethostbyname函数时不止返回IP信息,同时还带着其他信息,域名转IP时只需关注h_addr_list。下面简要介绍下上述结构体中的各个成员:
- h_name:该变量存有官方域名,官方域名代表某一主页,但实际上,一些著名公司的域名并未使用官方域名注册。
- h_aliases:可以通过多个域名访问同一主页,同一IP可以绑定多个域名,因此,除官方域名外还可指定其他域名。
- 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给同一域名,利用多个服务器进行负载均衡,同样可以通过此变量获取IP地址信息。
调用gethostbyname函数后返回的hostent结构体变量结构如下图所示:
示例程序:
#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;
if (argc != 2)
{
printf("Usage : %s <addr>\n", argv[0]);
exit(1);
}
// 使用gethostbyname()函数获取主机信息
host = gethostbyname(argv[1]); // argv[1]是命令行输入的主机名或IP地址
if (!host)
{
error_handling("gethostbyname() 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");
// 打印所有IP地址
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;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
利用 IP 地址获取域名
#include<netdb.h>
struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family);
成功时返回hostent结构体地址,失败时返回NULL指针。
参数:
- addr:包含IP地址信息的in_addr结构体指针。为了同时传递IPv4地址之外的其他信息,该变量的类型声明未char指针。
- len:IP地址的长度,对于IPv4地址是4,对于IPv6地址是16。
- family:地址类型,对于IPv4地址是AF_INET,对于IPv6地址是AF_INET6。
示例程序:
#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);
int main(int argc, char *argv[])
{
int i;
struct hostent *host;
struct sockaddr_in addr;
if (argc != 2)
{
printf("Usage : %s <addr>\n", argv[0]);
exit(1);
}
memset(&addr, 0, sizeof(addr));
addr.sin_addr.s_addr = inet_addr(argv[1]);
// 将IP地址转换为域名
host = gethostbyaddr((char *)&addr.sin_addr, sizeof(addr.sin_addr), AF_INET);
if (!host)
error_handling("gethostbyaddr() 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;
}
void error_handling(const char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
基于 Windows 的实现
与 Linux 基本一致。
#include<winsock2.h>
struct hostent *gethostbyname(const char * name);
成功时返回hostent结构体地址,失败时返回NULL指针。
#include<netdb.h>
struct hostent * gethostbyaddr(const char * addr, int len, int type);
成功时返回hostent结构体地址,失败时返回NULL指针。
Windows 下的 gethostbyname 函数的示例程序
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
void ErrorHanding(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)
ErrorHanding("WSAStartup() error!");
host = gethostbyname(argv[1]);
if (!host)
ErrorHanding("gethostbyname() 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 ErrorHanding(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
编译:
gcc gethostbyname_win.c -lwsock32 -o gethostbyname
运行结果:
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 8>gethostbyname www.naver.com
Official name : www.naver.com.nheos.com
Aliases 1 : www.naver.com
Address type : AF_INET
IP addr 1 : 223.130.192.248
IP addr 2 : 223.130.200.236
IP addr 3 : 223.130.200.219
IP addr 4 : 223.130.192.247
Windows 下的 gethostbyname 函数的示例程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
void ErrorHanding(char *message);
int main(int argc, char *argv[])
{
WSADATA wsaData;
int i;
struct hostent *host;
struct sockaddr_in addr;
if (argc != 2)
{
printf("Usage : %s <addr>\n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
ErrorHanding("WSAStartup() error!");
memset(&addr, 0, sizeof(addr));
addr.sin_addr.s_addr = inet_addr(argv[1]);
host = gethostbyaddr((char *)&addr.sin_addr, sizeof(addr.sin_addr), AF_INET);
if (!host)
ErrorHanding("gethostbyaddr() 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 ErrorHanding(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
编译:
gcc gethostbyaddr_win.c -lwsock32 -o gethostbyaddr
运行结果:
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 8>ping www.bilibili.com
正在 Ping a.w.bilicdn1.com [59.36.228.21] 具有 32 字节的数据:
来自 59.36.228.21 的回复: 字节=32 时间=8ms TTL=55
来自 59.36.228.21 的回复: 字节=32 时间=12ms TTL=55
来自 59.36.228.21 的回复: 字节=32 时间=104ms TTL=55
来自 59.36.228.21 的回复: 字节=32 时间=11ms TTL=55
59.36.228.21 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 8ms,最长 = 104ms,平均 = 33ms
C:\Users\81228\Documents\Program\TCP IP Project\Chapter 8>gethostbyaddr 59.36.228.21
Official name : 21.228.36.59.broad.jm.gd.dynamic.163data.com.cn
Address type : AF_INET
IP addr 1 : 59.36.228.21
习题
(1)下列关于DNS说法错误的是?
a. 因为DNS存在,故可以用域名替代IP。
b. DNS服务器实际上是路由器,因为路由器根据域名决定数据路径。
c. 所有域名信息并非集中于一台DNS服务器,但可以获取某一DNS服务器中未注册的IP地址。
d. DNS服务器根据操作系统进行区分,Windows下的DNS服务器和Linux下的DNS服务器是不同的。
答:b、d。
(2)阅读如下对话,并说明东秀的解决方案是否可行。这些都是大家可以在大学计算机实验室验证的内容。
静洙:“东秀吗?我们学校网络中使用的默认DNS服务器发生了故障,无法访问我要投简历的公司主页!有没有办法解决?”
东秀:“网络连接正常,但DNS服务器发生了故障?”
静洙:“恩!有没有解决方法?是不是要去周围的网吧?”
东秀:“有那必要吗?我把我们学校的DNS服务器IP地址告诉你,你改一下你的默认DNS服务器地址。”
静洙:“这样可以吗?默认DNS服务器必须连接到本地网络吧!”
东秀:“不是!上次我们学校DNS服务器发生故障时,网管就给了我们其他DNS服务器的I地址呢。”
静洙:“那是因为你们学校有多台DNS服务器!”
东秀:“是吗?你的话好像也有道理。那你快去网吧吧!”
答:如果网络上没有特别的限制,可以将与本地网络相连的DNS服务器指定为其他完好的DNS服务器。因此,东秀提议的方法可能成为解决方法。也就是说,静洙可以不去网吧。
(3)在浏览器地址栏输入www.orentec.co.kr,并整理出主页显示过程。假设浏览器访问的默认DNS服务器中并没有关于www.orentec.co.kr的IP地址信息。
步骤1:计算机向默认DNS服务器询问IP地址
步骤2:默认DNS服务器没有IP地址信息,因此向DNS主机发出询问
步骤3:DNS查询服务器向更上级的DNS服务器查询
步骤4:DNS查询服务器将查到的域名对应的IP地址逐级返还给主机
步骤5:网络浏览器根据该IP访问对应的网站
步骤6:计算机与网站服务器建立HTTP连接,浏览器得到HTML等资源
步骤7:浏览器得到资源后,渲染网页