Indy TIdIcmpClient - 如何检测超时?

时间:2016-06-28 18:38:31

标签: delphi ping indy

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

我很难理解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;

2 个答案:

答案 0 :(得分:8)

Ping()退出时,ReplyStatus属性包含传递给AReplyStatus事件的OnReply参数的相同信息(您忽略该参数)。 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()参数FromIpAddress。您可能还需要关注ToIpAddressReplyStatusType字段,以确保响应实际来自预期的目标计算机。

如果发生超时,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;

试试这个:

var url = 'https://ct.soa-gw.canadapost.ca/rs/ship/price';
var auth = 'Basic BASE64ENCODEDUSERNAMEANDPASSWORD';
var xml = '' +
'<?xml version="1.0" encoding="utf-8"?>' +
'<mailing-scenario xmlns="http://www.canadapost.ca/ws/ship/rate-v3">' +
'<customer-number>1234567</customer-number>' +
'<parcel-characteristics><weight>1.0</weight></parcel-characteristics>' +
'<services><service-code>DOM.XP</service-code></services>' +
'<origin-postal-code>K2B8J6</origin-postal-code>' +
'<destination><domestic><postal-code>J0E1X0</postal-code></domestic></destination>' +
'</mailing-scenario>';

var header = {
    'Authorization': auth,
    'Content-Type': 'application/soap+xml;application/vnd.cpc.ship.rate-v3+xml',
    'Accept-language': 'en-CA',
    'Accept': 'application/vnd.cpc.ship.rate-v3+xml'
};

var r = nlapiRequestURL(url, xml, header, null, 'POST');

console.log(r.code[0]);
console.log(r.body);

答案 1 :(得分:0)

IdIcmpClient companent有OnReply事件。此事件的AReplyStatus参数类型为TReplyStatus。 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;