65.9K
CodeProject 正在变化。 阅读更多。
Home

多线程 DNS 测试器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (6投票s)

2012年11月3日

CPOL

5分钟阅读

viewsIcon

23449

downloadIcon

1206

这基本上是 Charles Putney 的 DNS 测试器,但也是多线程的,并且可以与静态列表一起使用

引言

如今新的 DNS 服务器都是多线程的,所以如果你想测试你全新的 DNS 服务器,并将其与你单线程的 DNS 服务器进行比较,这里有一个工具可以帮助你。

背景 

通常,DNS 服务器不同版本之间的区别在于一些安全修复。BIND 也是如此。例如,BIND 9 的每个版本都包含一些错误修复。所以在这方面,让 IT 管理员升级是很困难的,因为他们通常信任他们的防火墙机制和安全功能,或者他们只是不想处理新的配置。他们说的一部分是正确的,因为 BIND 版本只在错误修复上有所不同。例如,市面上有很多 DNS 服务器使用 BIND 4.9,并且没有升级计划。

BIND 社区一直存在这个问题,因为他们必须为从版本 4 开始发布的每个 BIND 版本发布文档,每次发布安全修复时,更不用说他们必须处理所有 6 个主要版本来修复安全漏洞了。

我个人认为,自从 BIND 9 发布以来,情况已经发生了巨大变化。从这个版本开始,BIND 支持多线程,这意味着不再有排队。在此版本之前,如果你同时发送一打数据包,它会排队处理并逐个回复。但在 BIND 9 中,它内置了多线程功能。

我们在 IUT 想更新我们的 DNS 服务器,因为它们已经非常老旧了,所以我们决定在 FreeBSD 上使用 BIND。但实际上我们不知道使用哪个版本,因为最流行的版本是 8,而最新的版本是 9。

有一篇文章关于 9 版本是一个多线程 DNS 服务器,所以我们想测试一下性能提升。

根据我在一个 2 核服务器上进行的测试,前几个数据包在旧版本上响应更快,但当达到第 10 个或第 12 个数据包时,多线程 DNS 服务器的响应速度远远快于旧版本。

所以最后我基于 Charles Putney 的 DNS 测试器编写了这个程序。我希望它有用。

 

使用代码 

好吧,尽管代码行数发生了很大的变化,但那个已经很受欢迎的 DNS 测试器有一个非常整洁高效的测试 DNS 服务器的方法。它唯一的问题是它一次发送一个数据包,所以给服务器一些时间来处理每个数据包。

我认为这并不能真正测试新的多线程 DNS 服务器的能力。所以我重写了它来使用线程。

我创建了一个线程池,并更改了一些函数来代理,以便它们能够并行运行。这使得程序非常消耗内存,但我们说的是 20MB 的 RAM,对于这样的程序来说仍然很多,但我需要为每个线程复制远程主机列表和一些变量(如时间)。

例如,我创建了一个名为 dnscheck 的函数,它做了和以前一样的事情,除了它只处理自己的远程主机,向其发送数据包并等待响应。当然,它必须有自己的一份名称、时间等副本。

 正如你所见,它只接受一个对象。实际上它里面有所有东西,因为我传递了我为此目的创建的一个类作为它的变量。但必须是这样,因为对于多线程,它必须是一个委托函数,才能同时运行多次。

我还创建了一个虚拟委托函数。它所做的就是将我的对象传递给 dnscheck:

 private delegate void _dnscheck(object me);
 private void dnscheck(object me){} 

所以在这里,我为每个线程创建了委托函数的对象,并将我的函数作为参数传递。然后我初始化了我称之为 me 的类,其中包含所需的信息,然后我将其传递给我的委托函数的对象:

for (i=0;i<URLNamescount;i++)
{
_dnscheck ddnscheck = new _dnscheck(dnscheck);
me = new mine(URLNames, URLNamescount, DNSAddress1, DNSAddress2, i);
ThreadPool.QueueUserWorkItem(new WaitCallback(ddnscheck), me);
                
}     

我在显示异常信息到状态框时遇到了一些麻烦。因为在 .net 中,你不能正常访问在其他线程中创建的对象。你的函数必须是委托,并且你还必须询问该对象是否需要调用:

private delegate void _StatusBoxPrint(object obj);
private void StatusBoxPrint(object obj)
{
string LogText = (string)obj;
StatusBox.Items.Add(DateTime.Now+"  "+LogText);
StatusBox.TopIndex=StatusBox.Items.Count-1;
if (StatusBox.Items.Count > 5000)
{
StatusBox.Items.RemoveAt(0);
}
StatusBox.Update();
} 

我还向异常处理代码添加了下面这行:

StatusBox.Invoke(status,e.Message); 

并对 ResultView 做了同样的事情,这是一个 listview 对象,有一些严重的“童年问题”(这让我不得不硬着头皮学习这些多线程技巧,因为第一次启动程序时就生成了 57 个异常!):

 if (ResultView.InvokeRequired)
 {
 changeListViewItems my = new changeListViewItems(_changeListViewItems);
  ResultView.Invoke(my, mi);
                                    
}  

 这里的 changeListViewItems 是一个委托函数,它接收一个对象,然后将其传递给 my。

关注点

我真的很想体验一下多线程应用程序,我通过这个做到了,因为它处理这么多线程非常有趣。

这实际上是一项简单的任务:你必须创建一个委托函数,然后为每个线程创建一个实例。你还必须让你的函数只接受一个对象,因为它通常只传递一个对象。

我还了解到,如果你想在不是创建它的线程中使用在另一个线程中创建的对象,你必须调用该类对象的 invoke 函数,它会简单地给你访问该对象的权限,就像我上面提到的那样。

但最重要的事情发生在当我比较我们旧的单线程 BIND 和我们最新的 BIND 时。也许这是个巧合(它发生了十次!),但第一个数据包由旧的响应得更快,但最后一个数据包由新的响应得更快!这是我的测试图片。

第一个是多线程的,第二个是单线程的。

 

 

历史  

上传源代码:2012/11/3

更新源代码:2013/1/7 - 添加了压力测试以在压力条件下测试 DNS

添加二进制安装:2013/1/7 

更新:2013/1/11 - 修复了大量 bug - 现在有停止按钮和状态栏 

代码优化:2013/1/23 

© . All rights reserved.