5

I have a small piece of code that checks if a computer is alive by pinging it. We use to have a room with 40 computer and I wanna check remotely through my program which on is alive.

Therefore I wrote a little ping function using indy

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;

So I've developped that at home on my wifi network and everthing just work fine.

When I get back to work I tested and I get an error saying

Socket Errod # 10040 Message too long

At work we have fixed IPs and all the computer and I are in the same subnet.

I tried to disconnect from the fixed IP and connect to the wifi which of course is DHCP and not in the same subnet, and it is just working fine.

I have tried searching the internet for this error and how to solve it but didn't find much info.

Of course I have tried to change the default buffer size to a larger value but it didn't change anything I still get the error on the fixed IP within same subnet.

Moreover, I don't know if this can help finding a solution, but my code treats exceptions, but in that case it takes about 3-4 seconds to raise the error whereas the Timeout is set to 200 milliseconds. And I cannot wait that long over each ping.

By the way I use delphi 2010 and I think it is indy 10. I also have tested on XE2 but same error.

Any idea

----- EDIT -----

This question is answered, now I try to have this running in multithread and I have asked another question for that Delphi (XE2) Indy (10) Multithread Ping

Community
  • 1
  • 1
HpTerm
  • 8,151
  • 12
  • 51
  • 67
  • may there be firewall blocking ICMP activity ? – Arioch 'The Oct 04 '12 at 08:30
  • does TidIcmpClient.OnReply get called ? – Arioch 'The Oct 04 '12 at 08:32
  • *I have tried to change the default buffer size to a larger value* How exactly ? show the code ? Perhaps you'd better try to decrease it instead. [This example](http://citforum.ru/nets/articles/ping/) shows using Windows API to make pings and there buffer size is message size which reported being too large. [Delphi Forum](https://forums.embarcadero.com/thread.jspa?messageID=424596) also holds discussion of IdICMP ping – Arioch 'The Oct 04 '12 at 08:36
  • 2
    This code has a memory leak: if an exception occurs, the component will not be freed. And the owner should be nil as you want to free the component yourself in the same method. – mjn Oct 04 '12 at 11:40
  • @mjn You're right I Edit my code. – HpTerm Oct 04 '12 at 12:17

3 Answers3

7

Set the PacketSize property to 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;
LaKraven
  • 5,804
  • 2
  • 23
  • 49
  • 2 thing. First of all, it works by putting 24. Can you explain why ? Second thing, I still wait about 3-4 seconds, is there any way of reducing this time to 200-300 milliseconds ? Orelse I should use multithreding because waiting 40 x 4 = 160 seconds is way too long. And thanks by the way – HpTerm Oct 04 '12 at 09:06
  • I'm not all-together certain why it works... but I *believe* it's to do with the maximum size of an ICMP packet accepted by most devices (I'd have to look that up). You could try using an *even lower* `PacketSize` to see if this speeds up the result! – LaKraven Oct 04 '12 at 09:08
  • @HpTerm check "does TidIcmpClient.OnReply get called ? ",please do check it. Perhaps it would work soonner than your 4 seconds – Arioch 'The Oct 04 '12 at 09:44
  • @Archos Yes it is called after the 3-4 seconds. – HpTerm Oct 04 '12 at 10:16
  • 1
    Are you setting `AHost` to a hostname or an IP address? A hostname has to be resolved to an IP before any data can be sent to it, and that resolving can take extra time if your machine's DNS subsystem is not working properly or is running slow. There is no way to set a timeout on that hostname resolve because it is just a single API call to the OS. To set a timeout on it, you would have to resolve the hostname manually using `TIdDNSResolver` instead. – Remy Lebeau Oct 04 '12 at 21:26
  • 2
    As for the `PacketSize`, it is set to 1024 by default. Whatever data you pass to `Ping()` (which in this case is nothing) is used to fill in the space that `PacketSize` reserves in the ICMP packet. If there is no data, the space is still reserved in the packet. So reducing the `PacketSize` reduces the overall size of the ICMP packets. Smaller ICMP packets fit better in network packets ans thus are more routable. – Remy Lebeau Oct 04 '12 at 21:28
  • 5
    FYI, the default value of the `PacketSize` property will be reduced in a future Indy update to address this issue. – Remy Lebeau Oct 07 '12 at 03:53
  • @Remy thanks for all your answers. I assign an ipadress directly. Now I am working on doing it with thread but don't manage to have it running now. Not many examples available on the net. – HpTerm Oct 12 '12 at 10:52
  • @Remy you seem to be good at all that stuff, I have asked the following up of the current question where I try to have it multithread. Can you check here : http://stackoverflow.com/questions/12858551/delphi-xe2-indy-10-mutlithread-ping – HpTerm Oct 12 '12 at 11:58
  • @HpTerm: I posted an answer to it. – Remy Lebeau Oct 13 '12 at 00:17
  • Just stumbled over this and i want to add that a packet size of 24 is sufficent in most cases, but not always. You can get a reply from the gateway with a message like 'Host not reachable' where the answer is greater than your defined packetsize. I'd set it to the maximum possible MTU size. – Eggi Jan 05 '21 at 12:05
1

For XE5 and Indy10 this is still a problem, even with different Packet Size.

To answer the more cryptical fix:

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

This is a "magic" fix to get around the fact that there is a bug in the Indy10 component (if I have understood Remy Lebeau right).

My speculation is that this has some connection with the size of the receive buffer. To test my theory I can use any character and don't need to include the host address at all. Only use as many character you need for the receive buffer. I use this small code (C++ Builder XE5) to do a Ping with great success (all other values at their defaults):

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

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

As you can see I create a string of the same length as the PacketSize property. What you fill it with is insignificant.

Maybe this can be of help to @RemyLebeau when he work on the fix.

Max Kielland
  • 5,627
  • 9
  • 60
  • 95
0

use this code

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

MyIdIcmpClient.Ping(ABuffer);

Community
  • 1
  • 1
  • Can you give more details and explain why your code is answering the question ? If ABuffer is a string, why adding 255 times a space at the end of the string solves the problem ? – HpTerm Jan 23 '14 at 13:05