参考
- 《TCP/IP网络编程》 尹圣雨
域名及网络地址
域名系统
DNS是对IP地址和域名进行相互转换的系统,其核心是DNS服务器
域名
提供网络服务的服务器端是IP地址区分的,将容易记、易表述的域名分配并取代IP地址
DNS服务器
域名是赋予服务器的虚拟地址,域名服务器将虚拟地址转化为实际地址。所有计算机中都记录着默认DNS服务器地址
计算机内置的默认DNS服务器并不知道网络上所有域名的IP地址信息。若该DNS服务器无法解析,则会询问其他DNS服务器,并提供给用户(https://blog.csdn.net/beilizhang/article/details/119598903)
ping命令
ping命令用来验证IP数据报是否到达目的地,但执行过程中会同时经过“域名到IP地址”的转换过程,因此可以通过此命令查看IP地址
nslookup命令
在Linux系统中输入后,会提示进一步输入信息,此时可以输入server得到默认DNS服务器地址
IP地址和域名直接的转换
利用域名获取IP地址
#include <netdb.h>
struct hostent* gethostbyname(const char* hostname);
成功时返回hostent结构体地址,失败时返回NULL指针
返回时,地址信息装入hostent结构体
struct hostent
{
char* h_name; // official name
char** h_aliases // alias list
int h_addrtype; // host address type
int h_length; // address length
char** h_addr_list // address list
};
(1)h_name
该变量中存有官方域名(Official domain name)。官方域名代表某一主页,但实际上,一些著名公司的域名并未用官方域名注册
(2)h_aliases
可以通过多个域名访问同一主页。同一IP可以绑定多个域名,因此,除官方域名外还可指定其他域名。这些信息可以通过h_aliases获得
(3)h_addrtype
gethostbyname函数不仅支持IPv4,还支持IPv6。因此可以通过此变量获取保存在h_addr_list的IP地址的地址族信息。若是IPv4,则此变量存有AF_INET
(4)h_length
保存IP地址长度。若是IPv4地址,因为是4个字节,则保存4;IPv6时,因为是16个字节,故保存16
(5)h_addr_list
通过此变量以整数形式保存域名对应的IP地址。另外,用户较多的网络有可能分配多个IP给同一域名,利用多个服务器进行负载均衡。此时同样可以通过此变量获取IP地址信息。h_addr_list指向字符串指针数组(由多个字符串地址构成的数组)。字符串指针数组中的元素实际指向的是(实际保存的是)in_addr结构体变量地址值而非字符串
利用域名获取IP地址示例
#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])); // 输出IP地址信息
}
return 0;
}
void error_handling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
利用IP地址获取域名
#include <netdb.h>
struct hostent* gethostbyaddr(const char* addr, socklen_t len, int family);
成功时返回hostent结构体变量地址值,失败时返回NULL指针
(1)addr
含有IP地址信息的in_addr结构体指针。为了同时传递IPv4地址之外的其他信息,该变量的类型声明为char指针
(2)len
向第一个参数传递的地址信息的字节数,IPv4时为4,IPv6时为6
(3)family
传递地址族信息,IPv4时位AF_INET,IPv6时为AF_INET6
利用IP地址获取域名示例
#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);
}
基于Windows的实现
Windows平台下也有同名函数,与Linux中没有区别。但在最新的VS下已不能使用,需要使用<WS2tcpip.h>中的getaddrinfo()和getnameinfo()
利用域名获取IP地址
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
void ErrorHandling(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)
{
ErrorHandling("WSAStartup() error!");
}
host = gethostbyname(argv[1]);
if (!host)
{
ErrorHandling("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]));
}
WSACleanup();
return 0;
}
void ErrorHandling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
利用IP地址获取域名
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
void ErrorHandling(char* message);
int main(int argc, char* argv[])
{
WSADATA wsaData;
int i;
struct hostent* host;
SOCKADDR_IN addr;
if (argc != 2)
{
printf("Usage : %s <IP>\n", argv[0]);
exit(1);
}
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
ErrorHandling("WSAStartup() error!");
}
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)
{
ErrorHandling("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]));
}
WSACleanup();
return 0;
}
void ErrorHandling(char* message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}