什么是并行ping许多网络设备的最佳方法?

我通过迭代ping在网络中调查了很多设备(超过300个)。 程序按顺序轮询设备,因此速度很慢。 我想提高民意调查的速度。 在Delphi 7中有一些方法可以做到这一点: 每个设备都有一个执行ping的线程。手动管理线程。 学习和使用Indy 10.需要举例。 根据窗口消息使用重叠I / O. 根据事件使用完成端口。 什么是更快,更容易?请举例说明一些示例或链接。     
已邀请:
使用ICMP充斥网络并不是一个好主意。 您可能需要考虑某种线程池并对ping请求进行排队,并且有一定数量的线程在执行请求。     
我个人会选择IOCP。我在NexusDB中非常成功地使用它来进行传输实现。 如果要使用并行阻塞套接字和线程执行300个发送/接收周期,则最终需要300个线程。 使用IOCP,在将套接字与IOCP关联后,您可以执行300次发送操作,它们将在操作完成之前立即返回。操作完成后,所谓的完成包将排队到IOCP。然后,您有一个等待IOCP的线程池,操作系统会在完成数据包进入时唤醒它们。为了完成发送操作,您可以执行接收操作。接收操作也会立即返回,一旦实际完成,就会排队到IOCP。 关于IOCP的真正特殊之处在于它知道哪些线程属于它并且当前正在处理完成包。如果活动线程的总数(不在内核模式等待状态中)低于IOCP的并发数(默认情况下等于计算机上可用的逻辑核心数),则IOCP仅唤醒新线程。此外,如果有线程在IOCP上等待完成包(由于活动线程的数量等于并发数,尽管由于完成包排队而尚未启动),当前正在处理的其中一个线程一个完成包因任何原因进入内核模式等待状态,其中一个等待线程被启动。 返回IOCP的线程以LIFO顺序获取完成包。也就是说,如果一个线程正在返回IOCP并且仍有等待的完成包,那么该线程直接获取下一个完成包,而不是进入等待状态,并且该线程等待最长时间唤醒。 在最佳条件下,您将拥有多个线程,这些线程等于并发运行的可用核心数(每个核心上一个),拾取下一个完成包,处理它,返回IOCP并直接获取下一个完成包,所有这些都没有进入内核模式等待状态或线程上下文切换必须发生。 如果你有300个线程和阻塞操作,不仅会浪费至少300 MB的地址空间(对于堆栈的保留空间),但是当一个线程进入等待状态时,你也会有常量的线程上下文切换(等待)发送或接收完成)和完成发送或接收唤醒的下一个线程。 - Thorsten Engler 12小时前     
Windows上不推荐直接ICMP访问。可以直接访问Windows上的ICMP协议。由于恶意使用ICMP / ping / traceroute样式的原始套接字,我相信在某些版本的Windows上你需要使用Windows自带的api。特别是Windows XP,Vista和Windows 7不允许用户程序访问原始套接字。 我在ICMP.dll中使用了canned-functions,这是一些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演示的完整示例源代码。 更新在About.com上可以找到更现代的IPHLPAPI.DLL示例。     
这是Delphi3000的一篇文章,展示了如何使用IOCP创建线程池。我不是此代码的作者,但作者的信息在源代码中。 我在这里重新发布评论和代码:   现在每个人都应该明白什么   线程是,线程的原则   等等。对于那些有需要的人,   一个线程的简单功能就是   将处理从一个线程分离到   另一个,允许并发和   并行执行。主要原则   线程就像内存一样简单   已分配的,在之间引用   必须编组线程以确保   访问的安全性。有一些   其他原则,但这是真的   要关心的人。      而且......      线程安全队列将允许   要添加和删除的多个线程,   推送和弹出值   在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;
  因为这将记录到文件   将所有请求处理为单个   线程,但你可以做丰富的电子邮件   具有更高线程的通知   过程,甚至更好的过程   剖析正在发生的事情或   在我的程序中的步骤,我会   在另一篇文章中证明了这一点   一个人现在已经很久了。      现在我会告诉你这个,   享受..如果有的话发表评论   任何人都被困在一起。      克里斯     
您是否需要来自网络上每台机器的响应,或者这300台机器只是大型网络的一部分? 如果您需要来自每台计算机的响应,则可以考虑使用广播地址或多播地址作为回应请求。     
请尝试Linux的“chknodes”并行ping,它将向您的网络的所有节点发送单个ping。如果指定的话,它也会执行反向查找并请求http响应。它完全用bash编写,即您可以轻松检查或根据您的需要进行修改。这是帮助的打印输出: chknodes -h chknodes ----快速并行ping chknodes [-l | --log] [-h | --help] [-H | --http] [-u | --uninstall] [-v | --version] [-V | --verbose] -l | --log记录到文件   -h | --help显示此帮助屏幕   -H | --http检查http响应   -n | --names获取主机名   -u | --uninstall删除安装   -v | --version显示版本   -V | --verbose显示每个IP地址被ping 您需要为它执行权限(与任何sh / bash脚本一样)才能运行它:
chmod +x chknodes
在第一次运行即
./chknodes
它会建议将自己安装到/ usr / local / bin / chknodes,然后再给它
chknodes
就足够了。你可以在这里找到它: www.homelinuxpc.com/download/chknodes     

要回复问题请先登录注册