域名系统

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. Employees Earning More Than Their Managers

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  2. python之其他模块的用法

    1.时间模块   在Python中通常有三种表示时间的方式,分别是时间戳.元组.格式化的时间字符串. 时间模块的常用方法 time.sleep() #指定延迟时间 time.time() #当前时间的 ...

  3. javascript动态修改对象的属性名

    在做东钿业务系统的时候,经常碰到写很多重复的ajax对接,于是就想封装一个方法,但是接收data的字段名不一样,所以就需要用到动态对象属性名这个写法了.其实很简单.直接看一下代码吧.

  4. Compaq Visual Fortran生成静态库的方法及使用

    Compaq Visual Fortran 6.5生成lib静态库详细方法: 打开Compaq Visual Fortran,新建Fortran Static Library工程,命名为ForLib: ...

  5. Citrix-Netscaler-VPX-11.0

    平台: CentOS 类型: 虚拟机镜像 软件包: Citrix linux basic software Loadbalance security waf wlb 服务优惠价: 按服务商许可协议 云 ...

  6. vs移动团队项目集合

    vs移动团队项目集合: https://msdn.microsoft.com/zh-cn/library/vs/alm/dd936138(v=vs.120)/css

  7. cms-友情链接实现静态化

    service: package com.open1111.service.impl; import java.util.List; import javax.servlet.ServletConte ...

  8. Codeforces 763A. Timofey and a tree

    A. Timofey and a tree 题意:给一棵树,要求判断是否存在一个点,删除这个点后,所有连通块内颜色一样.$N,C \le 10^5$ 想法:这个叫换根吧.先求出一个点合法即其儿子的子树 ...

  9. pta 编程题13 File Transfer

    其它pta数据结构编程题请参见:pta 这道题考察的是union-find并查集. 开始把数组中每个元素初始化为-1,代表没有父节点.为了使树更加平衡,可以让每一个连通分量的树根的负值代表这个连通分量 ...

  10. Sql Server 表的复制

    声名:A,B ,都是表 --B表存在(两表结构一样)insert into B select * from A 若两表只是有部分(字段)相同,则 insert into B(col1,col2,col ...