域名系统

DNS是对IP地址和域名进行互相转换的系统,其核心是DNS服务器。提供网络服务的服务端也是通过IP地址来区分的,但由于IP地址难于记忆,因此通过容易记忆并表述的域名来取代IP地址

在浏览器地址栏输入www.baidu.com,或如图1-1用ping命令获取其IP地址,便可访问百度主页,那么通用域名访问和通过IP访问这二者有何区别?

图1-1   用ping命令获取百度地址

实际上,域名是赋予服务端的虚拟地址,而非实际地址。因此需要将虚拟地址转化为实际地址。那么,如何将域名转化为IP地址呢?DNS服务器承担此重任,可以向DNS服务器请求转换地址。所有计算机中都记录着默认DNS服务器地址,就是通过默认DNS服务器得到相应域名的IP地址信息。在浏览器地址栏中输入域名后,浏览器通过默认DNS服务器获取该域名对应的IP地址信息,之后才真正接入该网站

计算机内置的默认DNS服务器并不知道网络上所有域名的IP地址信息,若该DNS服务器无法解析,则会询问其他DNS服务器并提供给用户,如图1-2

图1-2   DNS和请求获取IP地址信息

图1-2展示了默认DNS服务器无法解析主机询问的域名对应IP地址时的应答过程,可以看出,默认DNS服务器收到自己无法解析的请求时,向上级DNS服务器询问,通过这种方式逐级向上传递信息,到达顶级DNS服务器时——根DNS服务器,它知道该向哪个DNS服务器询问,向下级DNS传递解析请求,得到IP地址后原路返回,最终将解析的IP地址传递到发起请求的主机,DNS就是这样层次化管理的一种分布式数据库系统

利用域名获取IP地址

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

  

只要向这个函数传递域名字符串,就会返回域名所对应的IP地址。只是返回时,地址信息装入hostent结构体,此结构体定义如下:

struct hostent
{
char *h_name; //正式主机名
char **h_aliases; //主机别名
int h_addrtype; //主机IP地址类型:IPV4-AF_INET
int h_length; //主机IP地址字节长度,对于IPv4是四字节,即32位
char **h_addr_list; //主机的IP地址列表
};

  

从上述结构体可以看出,当调用gethostbyname函数时不止返回IP信息,同时还带着其他信息,下面简要介绍下上述结构体中的各个成员:

  • 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结构体变量结构如图1-3所示:

图1-3   hostent结构体变量

下面我们看一下gethostbyname函数的应用,并说明hostent结构体变量的特性

gethostbyname.c

#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函数
  • 第20行:输出官方域名
  • 第21、22行:输出除官方域名以外的域名
  • 第24、25行:输出IP地址信息

编译gethostbyname.c并运行

# gcc gethostbyname.c -o gethostbyname
# ./gethostbyname www.taobao.com
Official name:www.taobao.com.danuoyi.tbcache.com
Aliases 1:www.taobao.com
Address type:AF_INET
IP addr 1:61.154.126.109
IP addr 2:218.67.61.254

  

大家可在编译gethostbyname.c后自行选择一个域名进行测试,现在我们看一下第24和25行,如果我们只看hostent结构体的定义,结构体成员h_addr_list指向了一个字符串指针数组(由多个字符串地址构成的数组)。但字符串指针数组中的元素实际指向的是in_addr结构体地址而非字符串,如图1-4所示:

图1-4   h_addr_list结构体成员

图1-4给出了h_addr_list结构体的参照关系,正因如此,上述代码中才有了第25行的类型转换,并调用inet_ntoa函数

这里可能大家会有疑问,为什么是char *而不是in_addr *?hostent结构体的成员h_addr_list指向的数组类型并不是in_addr结构体的指针数组,而是采用了char指针。是因为hostent结构体并非只为IPv4准备,因此考虑到通用性,声明为char指针类型的数组。那么,声明为void指针类型的数组是否更合理?确实,如果指针对象不明确时,声明为void指针类型更合理,但我们所学习的套接字函数是早在void指针标准化之前定义的,在当时无法确定指针类型时都采用char指针

利用IP地址获取域名

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

#include <sys/socket.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

现在我们来看一下gethostbyaddr函数的使用方法

gethostbyaddr.c

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

  

除第22行的gethostbyaddr函数调用外,与gethostbyname.c并无区别,因为函数调用的结果是通过hostent结构体变量地址值传递的

编译gethostbyaddr.c并运行

# gcc gethostbyaddr.c -o gethostbyaddr
# ./gethostbyaddr 218.67.61.254
Official name: 254.61.67.218.broad.sm.fj.dynamic.163data.com.cn
Address type: AF_INET
IP addr 1: 218.67.61.254

  

之前我们通过gethostbyname.c获得了某宝的IP地址,现在我们尝试用之前获得的IP地址反过来获取域名信息。从运行结果可以看到,记录于DNS的官方主页地址具有特殊格式

TCP/IP网络编程之域名及网络地址的更多相关文章

  1. 《TCP/IP网络编程》

    <TCP/IP网络编程> 基本信息 作者: (韩)尹圣雨 译者: 金国哲 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787115358851 上架时间:2014-6- ...

  2. TCP/IP网络编程 读书笔记1

    本篇主干内容是TCP/IP网络编程1-9章学习笔记 1. linux文件描述符 描述符从3开始以由小到大的顺序编号,0,1,2,分配给标准I/O用作标准输入.标准输出和标准错误. 2. 协议族与套接字 ...

  3. TCP/IP网络编程系列之三(初级)

    TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值, ...

  4. TCP/IP网络编程之多播与广播

    多播 多播方式的数据传输是基于UDP完成的,因此,与UDP服务端/客户端的实现非常接近.区别在于,UDP数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机.换言之,采用多播方式时 ...

  5. TCP/IP网络编程之套接字类型与协议设置

    套接字与协议 如果相隔很远的两人要进行通话,必须先决定对话方式.如果一方使用电话,另一方也必须使用电话,而不是书信.可以说,电话就是两人对话的协议.协议是对话中使用的通信规则,扩展到计算机领域可整理为 ...

  6. 浅谈TCP/IP网络编程中socket的行为

    我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: 1. TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) 2. Socket I/O系统 ...

  7. TCP/IP网络编程系列之四(初级)

    TCP/IP网络编程系列之四-基于TCP的服务端/客户端 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流的 ...

  8. TCP/IP网络编程系列之二(初级)

    套接字类型与协议设置 我们先了解一下创建套接字的那个函数 int socket(int domain,int type,int protocol);成功时返回文件描述符,失败时返回-1.其中,doma ...

  9. TCP/IP网络编程之多线程服务端的实现(二)

    线程存在的问题和临界区 上一章TCP/IP网络编程之多线程服务端的实现(一)的thread4.c中,我们发现多线程对同一变量进行加减,最后的结果居然不是我们预料之内的.其实,如果多执行几次程序,会发现 ...

随机推荐

  1. Java面向对象的练习。动物乐园

    本次项目是:以面向对象的思想设计动物乐园系统. 动物乐园中有猫,狗,鸭子等成员,还可以增加新成员. 猫和鸭子都有自己的名字,都有自己的腿,但腿的条数不同,猫和鸭子会发出叫声,猫的叫声是:喵喵喵……,鸭 ...

  2. hibernate课程 初探单表映射1-4 hibernate开发前准备

    开发前准备: 1 eclipse 2 hibernate tools的安装(需要相关的jar包)(可以简化orm框架) hibernate tools的安装步骤: 1 到官网下载 https://so ...

  3. 移植mavlink协议到STM32详细教程

    1准备材料, 首先准备一个带串口的stm32程序(这里选用整点原子的官方串口例程这里自己去找不讲)作者:恒久力行 QQ:624668529,然后去mavlink官网下载mavlink源码,这里重点讲解 ...

  4. UPDATE SQL 不同环境执行结果不一样

    背景:1.前台:JQUERY 提交数据 2.后台:OWIN C#  处理接收数据 3.数据库: postgresql ========================================= ...

  5. mybatis 异常处理:Invalid bound statement (not found)

    mybatis 的使用过程中提示错误: org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): ...

  6. 有些其他程序设置为从 Outlook 下载并删除邮件。为防止发生此意外情况,我们将这些邮件放入一个特殊的 POP 文件夹中

    最近使用FOXMAIL接收MSN邮件时,发现有一些邮件收取不到,进到WEB页面,页面下方提示“你的邮件位于 POP 文件夹中!有些其他程序设置为从 Outlook 下载并删除邮件.为防止发生此意外情况 ...

  7. 如何使用cPanel管理域名和数据库

    cPanel是一个基于web的基于web的控制面板,它简化了许多常见的系统管理任务,如网站创建.数据库部署和管理等.本指南向您展示了如何使用cPanel用户帐户管理域和数据库.所有这些指令都与位于端口 ...

  8. pta 编程题6 树的同构

    其它pta数据结构编程题请参见:pta 题目请参见:树的同构 因题目中左右子树是按照下标给出,因此用数组存放树是更好的方法. 判断两棵树是否同构:用递归的方法.如果当前两个结点都为空,则返回TRUE: ...

  9. xtrabackup 安装

    xtrabackup 安装   yum install -y perl-DBI perl-DBD-MySQL perl-Time-HiRes perl-IO-Socket-SSL  perl-Dige ...

  10. javascript同步和异步的区别与实现方式

    javascript语言是单线程机制.所谓单线程就是按次序执行,执行完一个任务再执行下一个. 对于浏览器来说,也就是无法在渲染页面的同时执行代码. 单线程机制的优点在于实现起来较为简单,运行环境相对简 ...