(点击上方公众号,可快速关注)
背景
互联网应用是通过IP标识通信双方的主机地址的。常用的IPv4是32位的整数,而这个数字不好记忆,所以引入了更加适合人类阅读和记忆的点分十进制表示法,如环回地址对应“127.0.0.1“。即使这样,本质上还是数字,仍然不利于沟通和记忆,试想以下,每次访问网站都要输入这样的地址,我们要记忆一大推这样的无意义的数字,抑或是随身带着一个笔记本,专门用来IP地址,这是多么不方便。
为了解决上面的问题,引入了域名(Domain Name)。域名可以看作是一个有意义的字符串和IP列表的映射。在通信过程中,先根据域名找到IP地址,之后的通信使用IP地址进行。将域名解析为IP地址的过程,就是域名解析。要实现域名解析,需要还需要两方面的支持:
映射存在何处
难不成每台电脑都保存千千万万的映射?当然不是。这些映射保存在DNS(Domain Name System)。全世界部署着很多DNS服务器节点,每次域名解析会根据规则请求这些DNS节点得到对应的IP地址。
解析函数
通常每个操作系统都提供相应的接口用于域名解析,如在Linux下是
gethostbyname
函数,ASIO底层使用该函数进行域名解析。
除了上面可读性的差异外,域名还提供了高可用的保障:一个域名可以对应多个IP地址,如果某个IP地址上的主机宕了,可以使用另外的IP进行通信。而且,也利于系统的平滑升级:若客户端和服务端通过通信,服务端升级到新机器,只要域名指向新机器,客户端不需要做改动。
由于IPv6的逐渐流行,实际解析的地址可能既包含老的IPv4地址,又包含IPv6地址,这点需要注意。下面给出域名解析的示例。
UDP域名解析
#include <boost/asio.hpp>
#include <iostream>
using namespace boost;
int main()
{
// 这里解析baidu的域名
std::string host = "www.baidu.com";
std::string port_num = "80";
asio::io_context ioc;
// 这步创建解析器
asio::ip::udp::resolver resolver(ioc);
boost::system::error_code ec;
asio::ip::udp::resolver::results_type results = resolver.resolve(host, port_num, ec);
if (ec.value() != 0) {
std::cout << "Failed to resolve a DNS name."
<< "Error code = " << ec.value()
<< ". Message = " << ec.message();
return ec.value();
}
// 打印IP地址
for (auto it = results.cbegin(); it != results.cend(); ++it) {
asio::ip::udp::endpoint const& ep = it->endpoint();
std::cout << ep << std::endl;
}
return 0;
}
上面的示例调用asio::ip::udp::resolver
的resolve
方法,得到asio::ip::udp::resolver::results_type
类型的结果。asio::ip::udp::resolver::results_type
本质上是个迭代器,可以通过迭代器的操作遍历。后面的循环打印每个endpoint。
这是程序调用的结果,包含IPv4和IPv6类型。
220.181.38.149:80
220.181.38.150:80
[240e:83:205:59:0:ff:b09b:159e]:80
[240e:83:205:58:0:ff:b09f:36bf]:80
上面的例子中有些类型是可以让编译器推断出的,之所以写出来是为了让类型明确出来。下面的TCP解析示例会使用auto
替代一些冗长的类型。
TCP域名解析
跟udp
版本完全一致,我们唯一要做的就是将asio::ip::udp::resolver
改为asio::ip::tcp::resolver
类型。
#include <boost/asio.hpp>
#include <iostream>
using namespace boost;
int main()
{
std::string host = "www.baidu.com";
std::string port_num = "80";
asio::io_context ioc;
asio::ip::tcp::resolver resolver(ioc);
boost::system::error_code ec;
auto results = resolver.resolve(host, port_num, ec);
if (ec.value() != 0) {
std::cout << "Failed to resolve a DNS name."
<< "Error code = " << ec.value()
<< ". Message = " << ec.message();
return ec.value();
}
for (auto it = results.cbegin(); it != results.cend(); ++it) {
auto const& ep = it->endpoint();
std::cout << ep << std::endl;
}
return 0;
}
喜欢我的文章,请关注我的公众号。