Indy TIdIcmpClient - 如何检测超时?

Indy TIdIcmpClient - how to detect a timeout?

(Using Delphi 2009) 我正在尝试使用 Indy TidIcmpClient 编写一个简单的网络连接监视器。这个想法是 ping 一个地址,然后在附加的 TidIcmpClient.OnReply 处理程序中测试返回了多少数据。如果回复包含 > 0 个字节,那么我知道连接成功,但如果 TidIcmpClient 超时或回复包含 0 个字节,我将假设 link 已关闭

我很难理解 TidIcmpClient 的逻辑,因为没有 'OnTimeout' 事件。

两个子题...

无论如何都会调用 TidIcmpClient.OnReply,无论是 (a) 收到数据时还是 (b) 达到超时时,以先到者为准?

如何区分由于超时而导致的零字节回复与超时期限内恰好包含零字节(或不可能发生)的真实回复?

换句话说,这种代码是否可以,或者我是否需要做其他事情来判断它是否超时

procedure TForm1.IdIcmpClient1Reply(ASender: TComponent; const AReplyStatus: TReplyStatus);
begin
if IdIcmpClient1.ReplyStatus.BytesReceived = 0 then
  //we must have timed out, link is down
else
  //got some data, connection is up
end;

procedure DoPing;
begin
IdIcmpClient1.ReceiveTimeout := 200;
IdIcmpClient1.Host := '8.8.8.8';
IdIcmpClient1.Ping;
end;

IdIcmpClient 组件有 OnReply 事件。此事件具有类型为 TReplyStatus 的 AReplyStatus 参数。 TReplyStatus 具有 ReplyStatusType 属性。 属性 的类型是 TReplyStatusTypes。 TReplyStatusTypes 具有 rsTimeOut 值。因此,将代码添加到 OnReply 事件并检查超时或其他错误。

procedure TForm1.IdIcmpClient1Reply(ASender: TComponent;
  const AReplyStatus: TReplyStatus);
begin
  if AReplyStatus.ReplyStatusType = rsTimeOut then
  begin
    //do someting on timeout.
  end;
end;

Ping() 退出时,ReplyStatus 属性 包含传递给 OnReply 事件的 AReplyStatus 参数的相同信息(您是忽略该参数)。 Ping() 只是在退出之前调用 OnReply 处理程序,将其传递给 ReplyStatus 属性,因此您实际上不需要在您的 OnReply 事件中使用例子。所做的只是不必要地分解您的代码。

procedure DoPing;
begin
  IdIcmpClient1.ReceiveTimeout := 200;
  IdIcmpClient1.Host := '8.8.8.8';
  IdIcmpClient1.Ping;
  // process IdIcmpClient1.ReplyStatus here as needed...
end;

也就是说,您没有正确处理 ReplyStatus 数据。即使 ping 失败,BytesReceived 字段也可以大于 0。顾名思义,它只是报告 ICMP 响应实际收到了多少字节。 ICMP 定义了许多不同类型的响应。 ReplyStatusType 字段将设置为实际收到的响应类型。定义了 20 个值:

type
  TReplyStatusTypes = (rsEcho,
    rsError, rsTimeOut, rsErrorUnreachable,
    rsErrorTTLExceeded,rsErrorPacketTooBig,
    rsErrorParameter,
    rsErrorDatagramConversion,
    rsErrorSecurityFailure,
    rsSourceQuench,
    rsRedirect,
    rsTimeStamp,
    rsInfoRequest,
    rsAddressMaskRequest,
    rsTraceRoute,
    rsMobileHostReg,
    rsMobileHostRedir,
    rsIPv6WhereAreYou,
    rsIPv6IAmHere,
    rsSKIP);

如果 ping 成功,ReplyStatusType 将为 rsEchoReplyData 字段将包含传递给 ABuffer 的(可选)数据Ping() 的参数。您可能还需要注意 FromIpAddressToIpAddress 字段,以确保响应实际上来自预期的目标机器。

如果发生超时,ReplyStatusType 将改为 rsTimeOut

试试这个:

procedure DoPing;
begin
  IdIcmpClient1.ReceiveTimeout := 200;
  IdIcmpClient1.Host := '8.8.8.8';
  IdIcmpClient1.Ping;
  if IdIcmpClient1.ReplyStatus.ReplyStatusType = rsEcho then
  begin
    // got some data, connection is up
  end
  else if IdIcmpClient1.ReplyStatus.ReplyStatusType = rsTimeout then
  begin
    // have a timeout, link is down
  end
  else
  begin
    // some other response, do something else...
  end;
end;