点击查看源码
getaddrinfo/gethostbyname等方法会到DNS服务器查询域名与IP地址的映射关系,查询过程中需要发送并接收UDP包进行数据交互,这个过程就会产生延时。实际上域名与IP地址的映射关系不会频繁变动,所有没有必要每次都到DNS服务器上查询域名与IP地址的映射关系,我们可以封装一个域名转IP地址的方法,添加一级本地DNS缓存,从而显著提高程序中域名转IP地址的速度。
实现DNS本地缓存前我们需要一把锁用来保存本地缓存,下面我们实现一个自旋锁类(用C++11原子操作实现)
//自旋锁类
class SpinMutex
{
private:
atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock()
{
while (flag.test_and_set(memory_order_acquire));
}
void unlock()
{
flag.clear(std::memory_order_release);
}
};
getaddrinfo/gethostbyname等函数的简单包装,提供更简单的接口
//将域名转换成IP地址
const char* GetHostAddress(const char* host, char* ip)
{
#ifndef _MSC_VER /*LINUX下的实现*/
struct addrinfo tmp;
struct addrinfo* res;
struct sockaddr_in* addr;
memset(&tmp, 0, sizeof(tmp));
tmp.ai_family = AF_INET;
tmp.ai_flags = AI_PASSIVE;
tmp.ai_socktype = SOCK_STREAM;
if (getaddrinfo(host, NULL, &tmp, &res) < 0) return NULL;
addr = (struct sockaddr_in*)(res->ai_addr);
ip = (char*)(inet_ntop(AF_INET, &addr->sin_addr, ip, 24));
freeaddrinfo(res);
#else /*WINDOWS下的实现*/
struct hostent* entry = gethostbyname(host);
if (entry == NULL) return NULL;
sprintf(ip, "%d.%d.%d.%d",
(entry->h_addr_list[0][0] & 0x00ff),
(entry->h_addr_list[0][1] & 0x00ff),
(entry->h_addr_list[0][2] & 0x00ff),
(entry->h_addr_list[0][3] & 0x00ff));
#endif
return ip;
}
添加本地DNS缓存,缓存有效时间暂定1分钟
//将域名转换成IP地址
string GetHostAddress(const string& host, bool cache = true)
{
static SpinMutex mtx;
static map<string, string> hostmap;
char buffer[64] = {0};
time_t now = time(NULL);
const time_t delay = 60;
static time_t uptime = 0;
//检查缓存是否超时
if (uptime + delay > now)
{
std::lock_guard<SpinMutex> lk(mtx);
hostmap.clear();
uptime = now;
}
//优先从缓存中查询
while (cache)
{
std::lock_guard<SpinMutex> lk(mtx);
auto it = hostmap.find(host);
if (it == hostmap.end()) break;
return it->second;
}
GetHostAddress(host.c_str(), buffer);
//更新缓存
if (cache)
{
std::lock_guard<SpinMutex> lk(mtx);
hostmap[host] = buffer;
}
return buffer;
}