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

发布于 2024-10-15 13:20:41 字数 267 浏览 3 评论 0原文

我通过迭代 ping 轮询网络中的许多设备(超过 300 个)。

该程序按顺序轮询设备,因此速度很慢。 我想提高轮询的速度。

在 Delphi 7 中有一些方法可以做到这一点:

  1. 每个设备都有一个执行 ping 操作的线程。手动管理线程。
  2. 学习和使用 Indy 10。需要示例。
  3. 使用基于窗口消息的重叠 I/O。
  4. 根据事件使用完成端口。

什么更快、更容易?请提供一些示例或链接。

I poll a lot of devices in network (more than 300) by iterative ping.

The program polls the devices sequentially, so it's slow.
I'd like to enhance the speed of polling.

There some ways to do this in Delphi 7:

  1. Each device has a thread doing ping. Manage threads manually.
  2. Learn and use Indy 10. Need examples.
  3. Use overlapped I/O based on window messages.
  4. Use completion ports based on events.

What is faster, easier? Please, provide some examples or links for example.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

意中人 2024-10-22 13:20:42

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 操作,没有任何问题。 (演示程序包含在下面的链接中)。

基于 ICMP.DLL 的演示的完整示例源代码位于此处。

更新 更现代的 IPHLPAPI.DLL 示例位于 About.com

Direct ICMP access is deprecated on windows. Direct access to the ICMP protocol on Windows is controlled. Due to malicious use of ICMP/ping/traceroute style raw sockets, I believe that on some versions of Windows you will need to use Windows own api. Windows XP, Vista, and Windows 7, in particular, don't let user programs access raw sockets.

I have used the canned-functionality in ICMP.dll, which is what some Delphi ping components do, but a comment below alerted me to the fact that this is considered "using an undocumented API interface".

Here's a sample of the main delphi ping component call itself:

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;

I believe that most modern Ping component implementations are going to be based on a similar bit of code to the one above, and I have used it to run this ping operation in a background thread, without any probems. (Demo program included in link below).

Full sample source code for the ICMP.DLL based demo is here.

UPDATE A more modern IPHLPAPI.DLL sample is found at About.com here.

剧终人散尽 2024-10-22 13:20:42

这里有一篇来自Delphi3000的文章展示了如何使用IOCP创建线程池。我不是这段代码的作者,但作者的信息在源代码中。

我在这里重新发布评论和代码:

现在大家应该明白什么了吧
线程是什么,线程的原理
等等。对于那些有需要的人来说,
线程的简单功能是
将处理从一个线程分离到
另一个,允许并发和
并行执行。主要原理
线程的数量也很简单,内存
分配的引用之间
必须对线程进行编组以确保
访问安全。有一些
其他原则但这确实是
值得关心的人。

还有……

线程安全队列将允许
添加和删​​除多个线程,
将值推入和弹出
按先开先关安全排队
基础。拥有高效、良好的
写队列你可以有一个高度
开发中有用的组件
线程应用程序,从帮助
使用线程安全日志记录,
异步处理请求。

线程池只是一个线程或一个
最多的线程数
常用于管理队列
请求。例如网络服务器
这将有一个连续的队列
需要处理的请求使用
线程池来管理http
请求,或者 COM+ 或 DCOM 服务器
使用线程池来处理rpc
请求。这样做是这样的
处理过程中的影响较小
向另一个请求,假设你跑了 3
同步请求和第一个
请求需要 1 分钟才能完成,
后两个请求无法完成
添加在上面至少 1 分钟
有自己的时间来处理,并且
大多数客户这不是
可以接受。

那么如何做到这一点..

从队列开始!!

Delphi确实提供了一个TQueue对象
这是可用的,但是
不幸的是不是线程安全的也不是
确实效率太高了,但是人
应该查看 Contnrs.pas 文件
看看borland如何在那里写堆栈和
队列。主要只有两个
队列所需的函数,这些
是添加和删除/推送和弹出。
添加/推送将添加一个值、指针或
对象到队列末尾。和
删除/弹出将删除并返回
队列中的第一个值。

您可以从 TQueue 对象派生
并覆盖受保护的方法和
添加关键部分,这将
给你一些帮助,但我会
希望我的队列等到新的
请求在队列中,并将
线程进入静止状态
等待新的请求。这可能是
通过添加互斥体或信号来完成
事件,但有一个更简单的方法。这
windows api 提供 IO 完成
队列为我们提供了线程
安全访问队列和状态
等待新请求时休息
队列。

实现线程池

线程池将会非常
简单并且可以管理 x 数量
所需线程并传递每个队列
请求提供的事件
已处理。很少有需要
实现一个 TThread 类和你的
要实现的逻辑和
封装在执行事件中
类,因此一个简单的
可以创建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;

因为这是记录到一个文件,所以它会
将所有请求处理为一个
线程,但你可以发送丰富的电子邮件
具有更高线程的通知
计算,甚至更好,处理
分析正在发生的事情或
我将在你的程序中执行以下步骤
在另一篇文章中演示如下
现在已经很长了。

现在我就留给你这个,
享受..如果有的话请发表评论
人们被困住的任何事情。

克里斯

Here's an article from Delphi3000 showing how to use IOCP to create a thread pool. I am not the author of this code, but the author's information is in the source code.

I'm re-posting the comments and code here:

Everyone by now should understand what
a thread is, the principles of threads
and so on. For those in need, the
simple function of a thread is to
separate processing from one thread to
another, to allow concurrent and
parallel execution. The main principle
of threads is just as simple, memory
allocated which is referenced between
threads must be marshalled to ensure
safety of access. There are a number
of other principles but this is really
the one to care about.

And on..

A thread safe queue will allow
multiple threads to add and remove,
push and pop values to and from the
queue safely on a First on First off
basis. With an efficient and well
written queue you can have a highly
useful component in developing
threaded applications, from helping
with thread safe logging, to
asynchronous processing of requests.

A thread pool is simply a thread or a
number of threads which are most
commonly used to manage a queue of
requests. For example a web server
which would have a continuous queue of
requests needing to be processed use
thread pools to manage the http
requests, or a COM+ or DCOM server
uses a thread pool to handle the rpc
requests. This is done so there is
less impact from the processing of one
request to another, say if you ran 3
requests synchronously and the first
request took 1 minute to complete, the
second two requests would not complete
for at least 1 minute adding on top
there own time to process, and for
most of the clients this is not
acceptable.

So how to do this..

Starting with the queue!!

Delphi does provides a TQueue object
which is available but is
unfortunately not thread safe nor
really too efficient, but people
should look at the Contnrs.pas file to
see how borland write there stacks and
queues. There are only two main
functions required for a queue, these
are add and remove/push and pop.
Add/push will add a value, pointer or
object to the end of a queue. And
remove/pop will remove and return the
first value in the queue.

You could derive from TQueue object
and override the protected methods and
add in critical sections, this will
get you some of the way, but I would
want my queue to wait until new
requests are in the queue, and put the
thread into a state of rest while it
waits for new requests. This could be
done by adding in Mutexes or signaling
events but there is an easier way. The
windows api provides an IO completion
queue which provides us with thread
safe access to a queue, and a state of
rest while waiting for new request in
the queue.

Implementing the Thread Pool

The thread pool is going to be very
simple and will manage x number of
threads desired and pass each queue
request to an event provided to be
processed. There is rarely a need to
implement a TThread class and your
logic to be implemented and
encapsulated within the execute event
of the class, thus a simple
TSimpleThread class can be created
which will execute any method in any
object within the context of another
thread. Once people understand this,
all you need to concern yourself with
is allocated memory.

Here is how it is implemented.

TThreadQueue and TThreadPool
implementation

(* 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. 

As you can see it's quite straight
forward, and with this you can
implement very easily any queuing of
requests over threads and really any
type of requirement that requires
threading can be done using these
object and save you a lot of time and
effort.

You can use this to queue requests
from one thread to multiple threads,
or queue requests from multiple
threads down to one thread which makes
this quite a nice solution.

Here are some examples of using these
objects.

Thread safe logging

To allow multiple
threads to asynchronously write to a
log file.

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;

As this is logging to a file it will
process all requests down to a single
thread, but you could do rich email
notifications with a higher thread
count, or even better, process
profiling with what’s going on or
steps in your program which I will
demonstrate in another article as this
one has got quite long now.

For now I will leave you with this,
enjoy.. Leave a comment if there's
anything people are stuck with.

Chris

山川志 2024-10-22 13:20:42

您是否需要网络上每台机器的响应,或者这 300 台机器只是更大网络的子集?

如果您需要每台机器的响应,您可以考虑使用广播地址 或 多播地址 用于您的回显请求。

Do you need a response from every machine on the network, or are these 300 machines just a subset of the larger network?

If you need a response from every machine, you could consider using a broadcast address or multicast address for your echo request.

梦明 2024-10-22 13:20:42

请尝试 Linux 的“chknodes”并行 ping,它将向网络的所有节点发送单个 ping。如果指定的话,它还会进行 dns 反向查找并请求 http 响应。它完全用 bash 编写,即您可以轻松检查它或根据您的需要修改它。以下是帮助的打印输出:

chknodes -h

chknodes ---- 快速并行 ping

chknodes [-l|--log] [-h|--help] [-H|--http] [-u|--uninstall ] [-v|--版本] [-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

就足够了。您可以在这里找到它:

www.homelinuxpc.com/download/chknodes

Please give a try on "chknodes" parallel ping for Linux which will send a single ping to all nodes of your network. It will do also dns reverse lookup and request http response if specified so. It's written completely in bash i.e. you can easily check it or modify it to your needs. Here is a printout of help:

chknodes -h

chknodes ---- fast parallel ping

chknodes [-l|--log] [-h|--help] [-H|--http] [-u|--uninstall] [-v|--version] [-V|--verbose]

-l | --log Log to file
-h | --help Show this help screen
-H | --http Check also http response
-n | --names Get also host names
-u | --uninstall Remove installation
-v | --version Show version
-V | --verbose Show each ip address pinged

You need to give execute right for it (like with any sh/bash script) in order to run it:

chmod +x chknodes

On the first run i.e.

./chknodes

it will suggest to install itself to /usr/local/bin/chknodes, after that giving just

chknodes

will be enough. You can find it here:

www.homelinuxpc.com/download/chknodes

羁拥 2024-10-22 13:20:41

用 ICMP 淹没网络并不是一个好主意。

您可能需要考虑某种线程池并对 ping 请求进行排队,并使用固定数量的线程来执行请求。

Flooding the network with ICMP is not a good idea.

You might want to consider some kind of thread pool and queue up the ping requests and have a fixed number of threads doing the requests.

谁许谁一生繁华 2024-10-22 13:20:41

我个人会选择 IOCP。我在 NexusDB 中的传输实现中非常成功地使用了它。

如果您想使用阻塞套接字和线程并行执行 300 个发送/接收周期,则最终需要 300 个线程。

使用IOCP,将套接字与IOCP关联后,您可以执行300个发送操作,并且它们将在操作完成之前立即返回。当操作完成时,所谓的完成包将在 IOCP 中排队。然后,您有一个线程池在 IOCP 上等待,操作系统会在完成数据包进入时唤醒它们。为了响应已完成的发送操作,您可以执行接收操作。接收操作也会立即返回,一旦实际完成,就会排队到 IOCP。

IOCP 的真正特殊之处在于它知道哪些线程属于它并且当前正在处理完成包。并且,只有当活动线程总数(不处于内核模式等待状态)低于 IOCP 的并发数(默认情况下等于机器上可用的逻辑核心数)时,IOCP 才会唤醒新线程。另外,如果有线程在 IOCP 上等待完成包(尽管完成包正在排队,但尚未启动,因为活动线程数等于并发数),当前正在处理的线程之一完成包由于任何原因进入内核模式等待状态,等待线程之一被启动。

返回 IOCP 的线程按 LIFO 顺序拾取完成包。也就是说,如果一个线程正在返回 IOCP,并且还有仍在等待的完成包,则该线程会直接拾取下一个完成包,而不是进入等待状态并唤醒等待时间最长的线程。

在最佳条件下,你将有等于可用核心数量的线程同时运行(每个核心一个),拾取下一个完成包,处理它,返回到IOCP并直接拾取下一个完成包,所有这些都无需进入内核模式等待状态或必须进行线程上下文切换。

如果您有 300 个线程并阻塞操作,那么您不仅会浪费至少 300 MB 地址空间(用于堆栈的保留空间),而且当一个线程进入等待状态(等待发送或接收完成),并且下一个线程已完成发送或接收唤醒。 – 托尔斯滕·恩格勒 12 小时前

Personally I would go with IOCP. I'm using that very successfully for the transport implementation in NexusDB.

If you want to perform 300 send/receive cycles using blocking sockets and threads in parallel, you end up needing 300 threads.

With IOCP, after you've associated the sockets with the IOCP, you can perform the 300 send operations, and they will return instantly before the operation is completed. As the operations are completed, so called completion packages will be queued to the IOCP. You then have a pool of threads waiting on the IOCP, and the OS wakes them up as the completion packets come in. In reaction to completed send operations you can then perform the receive operations. The receive operations also return instantly and once actually completed get queued to the IOCP.

The real special thing about an IOCP is that it knows which threads belong to it and are currently processing completion packages. And the IOCP only wakes up new threads if the total number of active threads (not in a kernel mode wait state) is lower than the concurrency number of the IOCP (by default that equals the number of logical cores available on the machine). Also, if there are threads waiting for completion packages on the IOCP (which haven't been started yet despite completion packages being queued because the number of active threads was equal to the concurrancy number), the moment one of the threads that is currently processing a completion package enters a kernel mode wait state for any reason, one of the waiting threads is started.

Threads returning to the IOCP pick up completion packages in LIFO order. That is, if a thread is returning to the IOCP and there are completion packages still waiting, that thread directly picks up the next completion package, instead of being put into a wait state and the thread waiting for the longest time waking up.

Under optimal conditions, you will have a number of threads equal to the number of available cores running concurrently (one on each core), picking up the next completion package, processing it, returning to the IOCP and directly picking up the next completion package, all without ever entering a kernel mode wait state or a thread context switch having to take place.

If you would have 300 threads and blocking operations instead, not only would you waste at least 300 MB address space (for the reserved space for the stacks), but you would also have constant thread context switches as one thread enters a wait state (waiting for a send or receive to complete) and the next thread with a completed send or receive waking up. – Thorsten Engler 12 hours ago

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文