我通过迭代ping轮询网络中的很多设备(超过300个)。
程序按顺序轮询设备,因此速度很慢。 我想提高民意调查的速度。
在Delphi 7中有一些方法可以做到这一点:
什么更快,更容易?请举例说明一些例子或链接。
答案 0 :(得分:9)
使用ICMP充斥网络不是一个好主意。
您可能需要考虑某种线程池并对ping请求进行排队,并且有一定数量的线程来处理请求。
答案 1 :(得分:6)
就个人而言,我会选择IOCP。我在NexusDB中非常成功地使用它进行传输实现。
如果要使用并行阻塞套接字和线程执行300个发送/接收周期,则最终需要300个线程。
使用IOCP,在将套接字与IOCP关联后,您可以执行300次发送操作,它们将在操作完成之前立即返回。操作完成后,所谓的完成包将排队到IOCP。然后,您有一个等待IOCP的线程池,操作系统会在完成数据包进入时唤醒它们。为了完成发送操作,您可以执行接收操作。接收操作也会立即返回,一旦实际完成,就会排队到IOCP。
关于IOCP的真正特殊之处在于它知道哪些线程属于它并且当前正在处理完成包。如果活动线程的总数(不在内核模式等待状态中)低于IOCP的并发数(默认情况下等于计算机上可用的逻辑核心数),则IOCP仅唤醒新线程。此外,如果有线程在IOCP上等待完成包(由于活动线程的数量等于并发数,尽管由于完成包排队而尚未启动),当前正在处理的其中一个线程完成包因任何原因进入内核模式等待状态,其中一个等待线程启动。
返回IOCP的线程以LIFO顺序获取完成包。也就是说,如果一个线程正在返回IOCP并且仍有等待的完成包,那么该线程直接获取下一个完成包,而不是进入等待状态,并且该线程等待最长时间唤醒。
在最佳条件下,您将拥有多个线程,这些线程等于并发运行的可用核心数(每个核心上一个),拾取下一个完成包,处理它,返回IOCP并直接拾取下一个完成包,都没有进入内核模式等待状态或必须进行线程上下文切换。
如果你有300个线程和阻塞操作,不仅会浪费至少300 MB的地址空间(对于堆栈的保留空间),但是当一个线程进入等待时你也会有不断的线程上下文切换状态(等待发送或接收完成)和完成发送或接收唤醒的下一个线程。 - Thorsten Engler 12小时前
答案 2 :(得分:5)
Windows上不推荐使用直接ICMP访问。可以直接访问Windows上的ICMP协议。由于恶意使用ICMP / ping / traceroute样式的原始套接字,我相信在某些版本的Windows上你需要使用Windows自带的api。特别是Windows XP,Vista和Windows 7不允许用户程序访问原始套接字。
我已经使用了ICMP.dll中的预制功能,这是一些Delphi ping组件所做的,但下面的评论提醒我这被认为是“使用未记录的API接口”这一事实。
以下是主要的delphi ping组件调用本身的示例:
function TICMP.ping: pIcmpEchoReply;
{var }
begin
// Get/Set address to ping
if ResolveAddress = True then begin
// Send packet and block till timeout or response
_NPkts := _IcmpSendEcho(_hICMP, _Address,
_pEchoRequestData, _EchoRequestSize,
@_IPOptions,
_pIPEchoReply, _EchoReplySize,
_TimeOut);
if _NPkts = 0 then begin
result := nil;
status := CICMP_NO_RESPONSE;
end else begin
result := _pIPEchoReply;
end;
end else begin
status := CICMP_RESOLVE_ERROR;
result := nil;
end;
end;
我相信大多数现代Ping组件实现都将基于与上面相似的代码,并且我已经使用它在后台线程中运行此ping操作,没有任何probems。 (演示程序包含在下面的链接中)。
基于ICMP.DLL的演示的完整示例源代码是here。
更新在About.com here.找到更现代的IPHLPAPI.DLL示例
答案 3 :(得分:4)
这是一个显示如何使用IOCP创建线程池的article from Delphi3000。我不是此代码的作者,但作者的信息在源代码中。
我在这里重新发布评论和代码:
现在每个人都应该明白什么 线程是,线程的原则 等等。对于那些有需要的人, 一个线程的简单功能就是 将处理从一个线程分离到 另一个,允许并发和 并行执行。主要原则 线程就像内存一样简单 已分配的,在之间引用 必须编组线程以确保 访问的安全性。有一些 其他原则,但这是真的 要关心的人。
然后......
线程安全队列将允许 要添加和删除的多个线程, 推送和弹出值 在First on First off上安全地排队 基础。有效率和良好 书面排队你可以有一个高度 发展中的有用成分 线程应用程序,从帮助 与线程安全日志记录,到 异步处理请求。
线程池只是一个线程或一个 最多的线程数 常用于管理队列 要求。例如,Web服务器 这将有一个连续的队列 需要处理的请求使用 线程池来管理http 请求,或COM +或DCOM服务器 使用线程池来处理rpc 要求。这样就完成了 处理一个人的影响较小 请求另一个,如果你跑了3 请求同步和第一个 请求花了1分钟完成, 第二个请求无法完成 至少1分钟加在上面 有时间处理,并为 这不是大多数客户 可以接受的。
那怎么做..
从队列开始!!
Delphi确实提供了一个TQueue对象 哪个可用但是 不幸的是,也没有线程安全 真的太有效了,但人 应该看看Contnrs.pas文件 看看borland怎么写堆栈和 队列。主要只有两个 这些是队列所需的功能 添加和删除/推送和弹出。 添加/推送将添加值,指针或 对象到队列的末尾。和 remove / pop将删除并返回 队列中的第一个值。
您可以从TQueue对象派生 并覆盖受保护的方法和 添加关键部分,这将 给你一些方法,但我愿意 希望我的队列等到新的 请求在队列中,然后放入 线程进入休息状态时 等待新的请求。这可能是 通过添加互斥锁或信令来完成 事件,但有一个更简单的方法。该 windows api提供IO完成 为我们提供线程的队列 安全访问队列和状态 在等待新的请求时休息 队列。
实施线程池
线程池将是非常的 简单并将管理x号 所需的线程并传递每个队列 请求提供的活动 处理。很少需要 实现一个TThread类和你的 要实施的逻辑和 封装在execute事件中 这个班,因此很简单 可以创建TSimpleThread类 这将执行任何方法 另一个上下文中的对象 线。一旦人们明白这一点, 所有你需要关心的 分配内存。
以下是它的实施方式。
TThreadQueue和TThreadPool 实施强>
(* Implemented for Delphi3000.com Articles, 11/01/2004
Chris Baldwin
Director & Chief Architect
Alive Technology Limited
http://www.alivetechnology.com
*)
unit ThreadUtilities;
uses Windows, SysUtils, Classes;
type
EThreadStackFinalized = class(Exception);
TSimpleThread = class;
// Thread Safe Pointer Queue
TThreadQueue = class
private
FFinalized: Boolean;
FIOQueue: THandle;
public
constructor Create;
destructor Destroy; override;
procedure Finalize;
procedure Push(Data: Pointer);
function Pop(var Data: Pointer): Boolean;
property Finalized: Boolean read FFinalized;
end;
TThreadExecuteEvent = procedure (Thread: TThread) of object;
TSimpleThread = class(TThread)
private
FExecuteEvent: TThreadExecuteEvent;
protected
procedure Execute(); override;
public
constructor Create(CreateSuspended: Boolean; ExecuteEvent: TThreadExecuteEvent; AFreeOnTerminate: Boolean);
end;
TThreadPoolEvent = procedure (Data: Pointer; AThread: TThread) of Object;
TThreadPool = class(TObject)
private
FThreads: TList;
FThreadQueue: TThreadQueue;
FHandlePoolEvent: TThreadPoolEvent;
procedure DoHandleThreadExecute(Thread: TThread);
public
constructor Create( HandlePoolEvent: TThreadPoolEvent; MaxThreads: Integer = 1); virtual;
destructor Destroy; override;
procedure Add(const Data: Pointer);
end;
implementation
{ TThreadQueue }
constructor TThreadQueue.Create;
begin
//-- Create IO Completion Queue
FIOQueue := CreateIOCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 0);
FFinalized := False;
end;
destructor TThreadQueue.Destroy;
begin
//-- Destroy Completion Queue
if (FIOQueue <> 0) then
CloseHandle(FIOQueue);
inherited;
end;
procedure TThreadQueue.Finalize;
begin
//-- Post a finialize pointer on to the queue
PostQueuedCompletionStatus(FIOQueue, 0, 0, Pointer($FFFFFFFF));
FFinalized := True;
end;
(* Pop will return false if the queue is completed *)
function TThreadQueue.Pop(var Data: Pointer): Boolean;
var
A: Cardinal;
OL: POverLapped;
begin
Result := True;
if (not FFinalized) then
//-- Remove/Pop the first pointer from the queue or wait
GetQueuedCompletionStatus(FIOQueue, A, Cardinal(Data), OL, INFINITE);
//-- Check if we have finalized the queue for completion
if FFinalized or (OL = Pointer($FFFFFFFF)) then begin
Data := nil;
Result := False;
Finalize;
end;
end;
procedure TThreadQueue.Push(Data: Pointer);
begin
if FFinalized then
Raise EThreadStackFinalized.Create('Stack is finalized');
//-- Add/Push a pointer on to the end of the queue
PostQueuedCompletionStatus(FIOQueue, 0, Cardinal(Data), nil);
end;
{ TSimpleThread }
constructor TSimpleThread.Create(CreateSuspended: Boolean;
ExecuteEvent: TThreadExecuteEvent; AFreeOnTerminate: Boolean);
begin
FreeOnTerminate := AFreeOnTerminate;
FExecuteEvent := ExecuteEvent;
inherited Create(CreateSuspended);
end;
procedure TSimpleThread.Execute;
begin
if Assigned(FExecuteEvent) then
FExecuteEvent(Self);
end;
{ TThreadPool }
procedure TThreadPool.Add(const Data: Pointer);
begin
FThreadQueue.Push(Data);
end;
constructor TThreadPool.Create(HandlePoolEvent: TThreadPoolEvent;
MaxThreads: Integer);
begin
FHandlePoolEvent := HandlePoolEvent;
FThreadQueue := TThreadQueue.Create;
FThreads := TList.Create;
while FThreads.Count < MaxThreads do
FThreads.Add(TSimpleThread.Create(False, DoHandleThreadExecute, False));
end;
destructor TThreadPool.Destroy;
var
t: Integer;
begin
FThreadQueue.Finalize;
for t := 0 to FThreads.Count-1 do
TThread(FThreads[t]).Terminate;
while (FThreads.Count > 0) do begin
TThread(FThreads[0]).WaitFor;
TThread(FThreads[0]).Free;
FThreads.Delete(0);
end;
FThreadQueue.Free;
FThreads.Free;
inherited;
end;
procedure TThreadPool.DoHandleThreadExecute(Thread: TThread);
var
Data: Pointer;
begin
while FThreadQueue.Pop(Data) and (not TSimpleThread(Thread).Terminated) do begin
try
FHandlePoolEvent(Data, Thread);
except
end;
end;
end;
end.
你可以看到它很直接 向前,你可以 很容易实现任何排队 请求线程,真的任何 需要的要求类型 可以使用这些来完成线程化 对象并为您节省大量时间 努力。
您可以使用它来排队请求 从一个线程到多个线程, 或来自多个的队列请求 线程到一个线程,使得 这是一个非常好的解决方案。
以下是使用这些的一些示例 对象。
线程安全日志记录
允许多个 线程以异步方式写入 日志文件。
uses Windows, ThreadUtilities,...;
type
PLogRequest = ^TLogRequest;
TLogRequest = record
LogText: String;
end;
TThreadFileLog = class(TObject)
private
FFileName: String;
FThreadPool: TThreadPool;
procedure HandleLogRequest(Data: Pointer; AThread: TThread);
public
constructor Create(const FileName: string);
destructor Destroy; override;
procedure Log(const LogText: string);
end;
implementation
(* Simple reuse of a logtofile function for example *)
procedure LogToFile(const FileName, LogString: String);
var
F: TextFile;
begin
AssignFile(F, FileName);
if not FileExists(FileName) then
Rewrite(F)
else
Append(F);
try
Writeln(F, DateTimeToStr(Now) + ': ' + LogString);
finally
CloseFile(F);
end;
end;
constructor TThreadFileLog.Create(const FileName: string);
begin
FFileName := FileName;
//-- Pool of one thread to handle queue of logs
FThreadPool := TThreadPool.Create(HandleLogRequest, 1);
end;
destructor TThreadFileLog.Destroy;
begin
FThreadPool.Free;
inherited;
end;
procedure TThreadFileLog.HandleLogRequest(Data: Pointer; AThread: TThread);
var
Request: PLogRequest;
begin
Request := Data;
try
LogToFile(FFileName, Request^.LogText);
finally
Dispose(Request);
end;
end;
procedure TThreadFileLog.Log(const LogText: string);
var
Request: PLogRequest;
begin
New(Request);
Request^.LogText := LogText;
FThreadPool.Add(Request);
end;
由于这是记录到文件,它会 将所有请求处理为单个 线程,但你可以做丰富的电子邮件 具有更高线程的通知 过程,甚至更好的过程 剖析正在发生的事情或 在我的程序中的步骤,我会 在另一篇文章中证明了这一点 一个人现在已经很久了。
现在我会告诉你这个, 享受..如果有的话发表评论 任何人都被困在了。
克里斯
答案 4 :(得分:3)
您是否需要网络上每台机器的响应,或者这300台机器只是大型网络的一部分?
如果您需要每台计算机的回复,可以考虑使用broadcast address或multicast address作为回应请求。
答案 5 :(得分:2)
请尝试Linux的“chknodes”并行ping,它会向您网络的所有节点发送一次ping。如果指定的话,它也会执行反向查找并请求http响应。它完全用bash编写,即您可以轻松检查或根据您的需要进行修改。这是帮助的打印输出:
chknodes -h
chknodes ----快速并行ping
chknodes [-l | --log] [-h | --help] [-H | --http] [-u | --uninstall] [-v | --version] [-V | - 详细]
-l | --log记录到文件 -h | --help显示此帮助屏幕 -H | --http检查http响应 -n | --names获取主机名 -u | --uninstall删除安装 -v | --version显示版本 -V | --verbose显示每个被ping的IP地址
你需要为它执行权限(就像使用任何sh / bash脚本一样)才能运行它:
chmod +x chknodes
第一次运行即
./chknodes
它会建议将自己安装到/ usr / local / bin / chknodes,之后只需
chknodes
就够了。你可以在这里找到它: