Part 1 start network programming:chapter EIGHT:域名及地址名

第八章域名及地址名

现在可能无人不知DNS,多多少少都听过,这到底是是个啥东西呢,与我们之前学的有什么关系呢?

8.1 域名系统

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

8.1.1 域名是啥

提供网络服务的服务器端也有自己的IP地址,但是IP地址很难记住,而类似www.xxx.com的域名却是很容易记住的,
我们在客户端需要与服务器端进行连接,通常用域名取代IP地址。

8.1.2 DNS服务器

在浏览器中输入Naver网站的IP地址222.122.195.5就能浏览这个网站。如果用www.naver.com访问网站,也能够得到相同的效果。

从结果上来看,这两种方式是没有区别的,但是接入的过程不同。
域名是赋给服务器的虚拟地址,并非真实,因此需要将虚拟地址转化为实际地址。如何把域名变为IP地址呢

这就需要DNS服务器了,本质上说DNS服务器是一个分布式数据库,里面存放着各种IP地址与域名对应的关系。

其过程是这样的:
计算机中记录着默认DNS服务器的地址,通过这个more你的DNS服务器得到相应域名的IP地址信息。在浏览器中输入域名后,浏览区通过默认DNS服务器获得该域名对应的IP地址信息,之后再接入网站。

注意:IP服务器的IP地址会相对频繁的改变,而域名一般不会轻易改变,因此域名是非常重要的,这类似于一个标识。

使用ping命令可以得到某个域名的IP地址信息,在控制台中运行即可

ping www.baidu.com

使用 nslookup可以得到自己计算机中注册的 默认DNS服务器地址

默认DNS服务器是啥?

计算机内置的默认DNS服务器并不知道网络上所有域名的IP地址信息,它只知道部分,一丢丢的信息。
如果这个服务器无法解析域名(没有你输入域名的IP地址信息),它会询问其他DNS服务器,并提供给用户。过程如下图所示。
在这里插入图片描述
上图是默认DNS服务器无法解析主机询问的域名IP地址时 的应答过程。
它会向上级DNS服务器询问,通过这种方式逐级向上传递信息,到达顶级DNS服务器——DNS根服务器。 这时它至少该向哪个DNS服务器询问,向下级DNS传递解析请求,得到IP地址后原路返回,最后将解析的IP地址传递到发起请求的主机,DNS就是这样层次化管理的一种分布式数据库系统。

8.2 IP地址和域名之间的转换

8.2.1 转换的必要性

假设我们现在需要运营一个www.xxx.com域名的公司,需要开发客户端使用公司提供的服务,这个客户端需要接入如下的服务器地址:IP:211.102.112.11 PORT:9999
首先,我们不能让用户每次使用都自己输入服务器的地址来连接吧~所以我们要在客户端里保存一个能够连接到服务器的地址信息。

由于IP地址会经常变更,相对而言域名要稳定许多,所以使用域名来编写程序是更优的选择。这样每次运行程序时,根据域名获取IP地址,再接入服务器,这样就不会依赖于IP地址的固定性了

因此我们需要一个能够在程序中将 域名 转换为 IP地址 的转换函数。

8.2.2 利用域名获得IP地址

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

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

使用时只需要传入一个字符串格式的域名,就能得到域名对应的IP地址
但是在返回时,地址信息转入了hostent结构体中。下面介绍一下这个结构体。

struct hostent
{
	char* h_name;		//official name
	char** h_aliases;	//alias list
	int h_haddrtype;	// host address type
	int h_length;		// address length 
	char** h_addr_list;	//address list
}
h_name:		变量中存放官方域名(Official domain name)。官方域名代表某一个主页,实际上一些公司的域名并未用官方域名注册。
haliases:	通过多个域名可以访问同一个主页,同一个IP也可以绑定多个域名。除了官方域名之外的其他域名存储在这个变量中。
h_addrtype:	gethostbyname函数不尽之处IPV4还支持IPV6.可以通过此变量获得保存h_addr_list的**IP地址的地址族**信息。如果是IPV4 则为 AF_INET
h_length:	保存IP地址的长度,如果是IPV4地址,4个字节。IPV6 为16个字节。
h_addr_list:最重要的成员。以整数形式保存域名对应的IP地址。  另外,用户多的网站可能分配多个IP给同一个域名,利用多服务器进行负载均衡。同样可以通过此变量获取IP地址信息。

调用gethostbyname函数后返回的hostent结构体的变量结构如下图所示!
在这里插入图片描述
下面用一个示例演示gethostbyname函数的使用,并说明hostent结构体变量的特性。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.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("Officail name: %s\n",host->name);

	// 别名
	for(int i = 0;host->h_aliases[i]){
		printf("第%d个别名(aliase)为: %s\n",i+1,host->h_aliases[i]);
	}

	// 地址类型
	printf("Address type:%s\n",(host->h_addrtype == AF_INET)?"AF_INET":"AF_INET6");

	// IP地址列表
	for (int i = 0; host->h_addr_list[i]; ++i)
	{
		/* code */
		printf("IP addr %d:%s\n",i+1,inet_addr(*(struct in_addr*)host->h_addr_list[i]));
	}
	return 0;
}

void error_handling(char* message)
{
	fputs(message,stderr);
	fputc('\n',stderr);
	exit(1);
}

先不看运行结果,大家心里都有数,先看一下程序中打印IP地址列表的部分。
printf(“IP addr %d:%s\n”,i+1,inet_addr((struct in_addr)host->h_addr_list[i]));

这里的类型转换是怎么回事?
在hostent类型的结构体中,其中一个元素是 char** h_addr_list;

h_addr_list[i] 的类型是 char* 为什么要强转成 in_addr*类型的指针呢?

因为这里char* 指针(这个字符指针数组)指向了(实际保存的是)in_addr结构体变量地址值而非字符串。
其结构如下图所示:
在这里插入图片描述

这里为什么要用char*?

因为通用性呀,如果用 in_addr* 只能在IPV4地址使用了。IPV6没法用了。

为什么不用void*?

这样当然更好,但是这个东西标准化出来的时候,规定的使用char* ,要是现在可能就用void* 了

这里是测试结果:
在这里插入图片描述

8.2.3 利用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

注意一下:下面的代码中,我们是通过IP get 域名和其他信息,输入参数是const char* addr,其指向in_addr 结构体变量,因此我们要初始化 要传入对应的 sin_addr的地址,同时要进行类型转换。

#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.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);
	}

	// 因为是IP 得到域名,输入参数中是结构体变量,因此先初始化变量。
	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("Officail name: %s\n",host->h_name);

	// 别名
	for(int i = 0;host->h_aliases[i];i++){
		printf("第%d个别名(aliase)为: %s\n",i+1,host->h_aliases[i]);
	}

	// 地址类型
	printf("Address type:%s\n",(host->h_addrtype == AF_INET)?"AF_INET":"AF_INET6");

	// IP地址列表
	for (int i = 0; host->h_addr_list[i]; ++i)
	{
		/* code */
		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);
}

下面是测试结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值