Delphi中的Indy Ping出现错误10040

7
我有一小段代码,通过ping命令检查计算机是否在线。我们曾经有一个房间里有40台电脑,我想通过我的程序远程检查哪台电脑在线。

因此,我使用indy编写了一个小的ping函数。

function TMainForm.Ping(const AHost : string) : Boolean;
var
  MyIdIcmpClient : TIdIcmpClient;
begin
  Result := True;

  MyIdIcmpClient := TIdIcmpClient.Create(nil);
  MyIdIcmpClient.ReceiveTimeout := 200;
  MyIdIcmpClient.Host := AHost;

  try
    MyIdIcmpClient.Ping;
    Application.ProcessMessages;
  except
    Result := False;
    MyIdIcmpClient.Free;
    Exit;
  end;
  if MyIdIcmpClient.ReplyStatus.ReplyStatusType <> rsEcho Then result := False;

  MyIdIcmpClient.Free;
end;

我在家里的Wi-Fi网络上开发了这个程序,一切都很顺利。

当我回到工作时进行测试时,出现了一个错误提示。

Socket Errod # 10040 Message too long

在工作中,我们有固定的IP地址,所有电脑和我都在同一子网内。

我尝试断开固定IP连接并连接到Wi-Fi,当然是DHCP,不在同一子网内,这样就可以正常工作。

我尝试在互联网上搜索此错误以及如何解决它,但没有找到太多信息。

当然,我已经尝试将默认缓冲区大小更改为较大的值,但仍然在相同子网中使用固定IP时出现错误。

此外,我不知道这是否有助于找到解决方案,但我的代码处理异常,在这种情况下,需要大约3-4秒才能引发错误,而超时设置为200毫秒。我无法在每个ping上等待那么长时间。

顺便说一下,我使用delphi 2010,我认为它是indy 10。我也在XE2上进行了测试,但出现了相同的错误。

有任何想法吗?

----- 编辑 -----

这个问题已经得到解答,现在我想尝试在多线程中运行它,并为此提出了另一个问题。 Delphi (XE2) Indy (10) 多线程 Ping

可能有防火墙阻止 ICMP 活动吗? - Arioch 'The
TidIcmpClient.OnReply 会被调用吗? - Arioch 'The
我已经尝试将默认缓冲区大小更改为较大的值。具体是如何更改的?展示一下代码?也许你最好尝试减小它。这个例子展示了使用Windows API进行ping操作,其中缓冲区大小是消息大小,据报道过大。Delphi论坛还讨论了IdICMP ping。 - Arioch 'The
2
这段代码存在内存泄漏问题:如果发生异常,组件将不会被释放。而且在同一方法中你想要自己释放组件,所以所有者应该为 nil。 - mjn
@mjn 你是对的,我编辑了我的代码。 - HpTerm
3个回答

8

PacketSize 属性设置为 24

function TMainForm.Ping(const AHost : string) : Boolean;
var
  MyIdIcmpClient : TIdIcmpClient;
begin
  Result := True;

  MyIdIcmpClient := TIdIcmpClient.Create(self);
  MyIdIcmpClient.ReceiveTimeout := 200;
  MyIdIcmpClient.Host := AHost;
  MyIdIcmpClient.PacketSize := 24;
  MyIdIcmpClient.Protocol := 1;
  MyIdIcmpClient.IPVersion := Id_IPv4;

  try
    MyIdIcmpClient.Ping;
    // Application.ProcessMessages; // There's no need to call this!
  except
    Result := False;
    Exit;
  end;
  if MyIdIcmpClient.ReplyStatus.ReplyStatusType <> rsEcho Then result := False;

  MyIdIcmpClient.Free;
end;

我不完全确定它为什么有效...但我相信这与大多数设备接受的ICMP数据包的最大大小有关(我得查一下)。您可以尝试使用更低的“PacketSize”来查看是否可以加快结果! - LaKraven
1
你是将 AHost 设置为主机名还是 IP 地址?在发送任何数据之前,主机名必须解析为 IP,如果您的计算机 DNS 子系统不正常或运行缓慢,则解析可能需要额外的时间。因为它只是一个对操作系统的单个 API 调用,所以无法设置主机名解析的超时。要设置超时,您必须改用 TIdDNSResolver 手动解析主机名。 - Remy Lebeau
2
至于PacketSize,默认设置为1024。无论您传递给Ping()的数据是什么(在这种情况下是空的),都将用于填充PacketSize在ICMP数据包中保留的空间。如果没有数据,则仍在数据包中保留该空间。因此,减小PacketSize会减小ICMP数据包的总大小。较小的ICMP数据包更适合网络数据包,因此更易路由。 - Remy Lebeau
5
FYI,为解决此问题,将在未来的 Indy 更新中降低“PacketSize”属性的默认值。 - Remy Lebeau
请注意,TIdIcmpClient在2012年12月进行了更新(就在我上次在这里发表评论后不久),不再使用PacketSize来分配所使用的缓冲区。现在,缓冲区是根据传递给Ping()的用户定义数据的大小进行分配的,而PacketSize现在仅作为最大大小限制。因此,如果您不发送任何用户数据,缓冲区中不会分配任何额外空间。 - undefined
显示剩余7条评论

1
对于XE5和Indy10来说,即使使用不同的数据包大小,仍然存在此问题。
回答更加神秘的解决方案:
ABuffer := MyIdIcmpClient1.Host + StringOfChar(' ', 255);

这是一个“神奇”的解决方法,以绕过Indy10组件中存在的一个bug(如果我理解Remy Lebeau正确的话)。

我推测这与接收缓冲区的大小有关。为了测试我的理论,我可以使用任何字符,并且根本不需要包括主机地址。只需使用足够接收缓冲区所需的字符数即可。我使用这个小代码(C++ Builder XE5)非常成功地进行Ping操作(所有其他值都保持默认):

AnsiString Proxy = StringOfChar('X',IcmpClient->PacketSize);

IcmpClient->Host = Host_Edit->Text;
IcmpClient->Ping(Proxy);

如您所见,我创建了一个与PacketSize属性长度相同的字符串。你填充它的内容并不重要。

或许这对@RemyLebeau在修复时有所帮助。


0
使用这段代码:

ABuffer := MyIdIcmpClient1.Host + StringOfChar(' ', 255);

MyIdIcmpClient.Ping(ABuffer);


你能否提供更多细节并解释一下你的代码为什么能回答这个问题?如果ABuffer是一个字符串,为什么在字符串末尾添加255个空格就能解决问题? - HpTerm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接