当前位置: 首页 > web >正文

8.渐入佳境 -- 域名及网络地址

前言

随着互联网用户的不断增加,现在DNS(Domain Name System,域名系统)几乎无人不知。人们也经常谈论DNS相关的专业话题。而且,不懂DNS的人用不了5分钟就能在网上搜索并学习DNS知识。网络的发展促使我们每个人都成了半个网络专家。

一、域名系统

DNS是对IP地址和域名进行相互转换的系统,其核心是DNS服务器。

1.什么是域名

提供网络服务的服务器端也是通过IP地址区分的,但几乎不可能以非常难记的IP地址形式交换服务器端地址信息。因此,将容易记、易表述的域名分配并取代IP地址。

2.DNS服务器

在浏览器地址栏中输人Naver网站的IP地址222.122.195.5即可浏览Naver网站主页。但我们通常输人Naver网站的域名www.naver.com访问网站。二者之间究竞有何区别?
从进人Naver网站主页这一结果上看,没有区别,但接人过程不同。域名是赋予服务器端的虚拟地址,而非实际地址。因此,需要将虚拟地址转化为实际地址。那如何将域名变为IP地址呢?
DNS服务器担此重任,可以向DNS服务器请求转换地址。
– “请问DNS服务器,www.naver.com的IP地址是多少?” –
所有计算机中都记录着默认DNS服务器地址,就是通过这个默认DNS服务器得到相应域名的IP地址信息。在浏览器地址栏中输人域名后,浏览器通过默认DNS服务器获取该域名对应的IP地址信息,之后才真正接人该网站。
计算机内置的默认DNS服务器并不知道网络上所有域名的IP地址信息。若该DNS服务器无法解析,则会询问其他DNS服务器,并提供给用户,如图:
在这里插入图片描述
展示了默认DNS服务器无法解析主机询问的域名IP地址时的应答过程。可以看出,默认DNS服务器收到自已无法解析的请求时,向上级DNS服务器询问。通过这种方式逐级向上传递信息,到达顶级DNS服务器—根DNS服务器时,它知道该向哪个DNS服务器询问。向下级DNS传递解析请求,得到IP地址后原路返回,最后将解析的IP地址传递到发起请求的主机。DNS就是这样层次化管理的一种分布式数据库系统。

二、IP地址和域名之间的转换

刚刚了域名和IP地址之间的转换过程,这次介绍通过程序向DNS服务器发出解析请求的方法。

1.程序中有必要使用域名吗?

“所有学习都要在开始前认识到其必要性!”这是我经常挂在嘴边的一句话。从语言的基本语法到系统函数,若无法回答“这到底有何必要?”学习过程将变得枯燥无味,而且很容易遗忘。
最头疼的是,学完之后很难应用。我们为什么需要将要讨论的转换函数?为了查看某一域名的IP地址吗?当然不是!下面通过示例解释原因。假设各位是运营www.SuperOrange.com域名的公司系统工程师,需要开发客户端使用公司提供的服务。该客户端需要接人如下服务器地址:
IP211.102.204.12,PORT 2012
应向程序用户提供便利的运行方法,因此,程序不能像运行示例程序那样要求用户输人IP和端口信息。那该如何将上述信息传递到程序内部?难道要直接将地址信息写人程序代码吗?当然,这样便于运行程序,但这种方案也有问题。系统运行时,保持IP地址并不容易。特别是依赖ISP服务提供者维护IP地址时,系统相关的各种原因会随时导致IP地址变更。虽然ISP会保证维持原有IP,但程序不能完全依赖于这一点。万一发生地址变更,就需要向用户进行如下解释:
“请卸载当前使用的程序,到主页下载并重新安装v1.2。”
那么,因为随时可能发生地址变更,所以向用户提供源代码,每次变更地址时让用户改变IP和端口号,并重新编译程序,这又如何?
IP地址比域名发生变更的概率要高,所以利用IP地址编写程序并非上策。还有什么办法呢?
一旦注册域名可能永久不变,因此利用域名编写程序会好一些。这样,每次运行程序时根据域名获取IP地址,再接人服务器,这样程序就不会依赖于服务器IP地址了。所以说,程序中也需要IP地址和域名之间的转换函数。

2.利用域名获取IP地址

使用以下函数可以通过传递字符串格式的域名获取IP地址。

#include <netdb.h>
struct hostent * gethostbyname(const char * hostname);
// 成功时返回hostent结构体地址,失败时返回NULL指针。

这个函数使用方便。只要传递域名字符串,就会返回域名对应的IP地址。只是返回时,地址信息装人hostent结构体。此结构体定义:

struct hostent
{char * h_name;//official namechar ** h_aliases;//alias listint h_addrtype;//host address typeint h_length;//address lengthchar ** h_addr_list;//address list
}

从上述结构体定义中可以看出,不只返回IP信息,同时还连带着其他信息。各位不用想得太过复杂。域名转IP时只需关注haddrlist。下面简要说明上述结构体各成员。
h_name : 该变量中存有官方域名(Official domain name)。官方域名代表某一主页,但实际上,一些著名公司的域名并未用官方域名注册。

h_aliases : 可以通过多个域名访问同一主页。同一IP可以绑定多个域名,因此,除官方域名外还可指定其他域名。这些信息可以通过haliases获得。

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结构体的变量结构如图,该图在实际编程中非常有用,希望大家结合之前的hostent结构体定义加以理解。
在这里插入图片描述
下列示例主要演示gethostbyname函数的应用,并说明hostent结构体变量的特性:

#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->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(char *message)
{fputs(message, stderr);fputc('\n',stderr);exit(1);
}

●第17行:将通过main函数传递的字符串用作参数调用gethostbyname。
●第21行:输出官方域名。
●第22、23行:输出除官方域名以外的域名。这样编写循环语句的原因可从上面图中找到答案。
●第26~28行:输出IP地址信息。但多了令人感到困惑的类型转换。关于这一点稍后将给出说明。
在这里插入图片描述
我利用Naver网站域名运行了上述示例,大家可以任选一个域名验。现在讨论上述示例的第26~28行。若只看hostent结构体的定义,结构体成员h_addr_list指向字符串指针数组(由多个字符串地址构成的数组)。但字符串指针数组中的元素实际指向的是(实际保存的是)in_addr结构体变量地址值而非字符串,如下图:
在这里插入图片描述
上图给出了h_addr_list结构体的参照关系。正因如此,示例的第28行需要进行类型转换,并调用inet_ntoa函数。另外,in_addr结构体的声明可以参考第3章。
– 为什么是char而不是in_addr
hostent结构体的成员h_addr_list指向的数组类型并不是in_addr结构体的
指针数组,而是采用了char指针。各位也许对这一点感到困惑,但我认为
大家应该能料到。hostent结构体并非只为IPv4准备。h_addr_list指向的
数组中也可以保存IPv6地址信息。因此,考虑到通用性,声明为char指针
类型的数组。
“声明为void指针类型是否更合理?"
若能想到这一点,说明对C语言掌握非常到位。当然如此。指针对象不明
确时,更适合使用void指针类型。但各位目前学习的套接字相关函数都是
在void指针标准化之前定义的,而当时无法明确指出指针类型时采用的是
char指针。

3.利用IP地址获取域名

之前介绍的gethostbyname函数利用域名获取包括IP地址在内的域相关信息。而gethostbyaddr函数利用IP地址获取域相关信息。

#include <netdb.h>
struct hostent * gethostbyaddr(const char * addr, socklen_t len, int family);
// 成功时返回hostent结构体变量地址值,失败时返回NULL指针。
// addr: 含有IP地址信息的in_addr结构体指针。为了同时传递IPv4地址之外的其他信息,该变量的类型声明为char指针。
// len: 向第一个参数传递的地址信息的字节数,IPv4时为4,IPv6时为16。
// family: 传递地址族信息,IPv4时为AF_INET,IPv6时为AF_INET6。

如果已经彻底掌握gethostbyname函数,那么上述函数理解起来并不难。下面通过示例演示该函数的使用方法。

#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",1+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",1+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);
}

在这里插入图片描述
emmmm,不知道为啥这个不成功,大家可以评论告诉我哪里错了😅


总结

大家五一劳动节假期快乐哦!

http://www.xdnf.cn/news/4061.html

相关文章:

  • Unity:Surface Effector 2D(表面效应器 2D)
  • OSE2.【Linux】练习:查找项目的main函数入口
  • 开元类双端互动组件部署实战全流程教程(第3部分:UI资源加载机制与界面逻辑全面解析
  • 事务隔离(MySQL)
  • FTP(文件传输协议)
  • 15.日志分析入门
  • LeetCode算法题 (反转链表)Day17!!!C/C++
  • Cookie与Session
  • JookDB:一款国产的通用数据库开发工具
  • 期末代码Python
  • 【数据结构】第八章:排序
  • 【言语理解】片段阅读之标题拟定(5)
  • ABC 404
  • TCP协议(三次握手、流量控制、拥塞控制)
  • 苹果公司正在与亚马逊支持的初创公司Anthropic展开合作
  • 解决DNS劫持问题
  • 【四人抢答器的设置mulisim14.0】2022-11-10
  • Java常用类
  • 51单片机入门教程——蜂鸣器播放天空之城
  • centos8源码安装openssl
  • Ubuntu安装编译环境
  • 使用 NGINX 实现 HTTP Basic 认证ngx_http_auth_basic_module 模块
  • Dify - Embedding Rerank
  • React状态管理
  • Java面试场景分析:从音视频到安全与风控的技术探讨
  • 怎么才能找到自己的天赋?
  • 09-24计算机考研408真题及答案
  • uniapp开发05-image标签的一些使用注意事项
  • GPIO引脚的上拉下拉以及转换速度到底怎么选
  • 使用注意力机制的seq2seq