《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址

《TCP/IP网络编程》学习笔记 | Chapter 8:域名及网络地址

《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 地址和域名之间的转换

使用域名的必要性

  1. 用户友好性: 域名通常比IP地址更易于记忆和识别,使用域名可以提高用户体验。
  2. 可扩展性: 域名允许在不更改程序代码的情况下更换服务器IP地址,这在服务器迁移或扩展时非常有用。
  3. 负载均衡: 通过域名,可以更容易地实现负载均衡,将请求分发到多个服务器上。
  4. 安全性: 域名可以与SSL/TLS证书结合使用,提供安全的HTTPS连接,保护数据传输的安全。
  5. 易于管理: 使用域名可以简化网络配置和维护,因为域名解析和管理通常比IP地址更简单。

利用域名获取 IP 地址

cpp 复制代码
#include<netdb.h>

struct hostent *gethostbyname(const char * hostname);

成功时返回hostent结构体地址,失败时返回NULL指针。

参数:

  • hostname:域名字符串。

hostent结构体定义如下:

cpp 复制代码
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结构体变量结构如下图所示:

示例程序:

cpp 复制代码
#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 地址获取域名

cpp 复制代码
#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。

示例程序:

cpp 复制代码
#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 基本一致。

cpp 复制代码
#include<winsock2.h>

struct hostent *gethostbyname(const char * name);

成功时返回hostent结构体地址,失败时返回NULL指针。

cpp 复制代码
#include<netdb.h>

struct hostent * gethostbyaddr(const char * addr, int len, int type);

成功时返回hostent结构体地址,失败时返回NULL指针。

Windows 下的 gethostbyname 函数的示例程序

cpp 复制代码
#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 函数的示例程序

cpp 复制代码
#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:浏览器得到资源后,渲染网页

相关推荐
qq_428639612 分钟前
植物明星大乱斗15
c++·算法·游戏
捕鲸叉33 分钟前
C++创建型模式之生成器模式
开发语言·c++·建造者模式
sxtyjty41 分钟前
人机打怪小游戏(非常人机)
c++
oioihoii1 小时前
单例模式详解
c++·单例模式·c#·大学必学
ikkkkkkkl1 小时前
深述C++模板类
开发语言·c++
Peter_chq1 小时前
【计算机网络】HTTP协议
linux·c语言·开发语言·网络·c++·后端·网络协议
vir022 小时前
好奇怪的游戏(BFS)
数据结构·c++·算法·游戏·深度优先·图论·宽度优先
kitesxian2 小时前
Leetcode128. 最长连续序列(HOT100)
c++
m0_675988232 小时前
Leetcode3244:新增道路查询后的最短距离 II(C++)
c++·算法·leetcode
胜天半子_王二_王半仙3 小时前
c++源码阅读__ThreadPool__正文阅读
开发语言·c++·开源