Jump to content

Fast Ping


Recommended Posts

Greetings All - I would like to quickly ping an array of instruments to determine their availability.   If the ping is successful I open up a tcpip port, transact and then close the port.  The problem is that the ping vi's I have experimented with do not respect the timeout parameter or have memory leak issues when opening and closing socket resources.  I have found an intermediate solution using a raw socket approach but sometimes the socket hangs under various circumstances.  Probably the cleanest solution would be to use the .net ping libraries if I could get the timeout to work correctly.  Any ideas?  BTW I am an admin on the test computer.

Link to comment

I use this on cRIO with the system exec vi:

timeout 0.1 ping -c1 127.0.0.1

Replace the 0.1 with the time you want (s) and the 127.0.0.1 with whatever IP address or hostname you want. I use this to determine if I want to attempt to open a shared variable connection to an expansion chassis since the timeouts on that API do not work.

Link to comment
5 hours ago, ShaunR said:

There is the IcmpSendEcho. It's fairly straight forward and is a one-function call.

Rolf used to have a ping library too. Not sure where that is now.

On Windows I recommend that function. ICMP is a low level protocol and can only be implemented in user space by using raw sockets, but that is a privileged resource that can only be opened by elevated processes on Windows, and by a process which is either root (UID=0) or has the CAP_NET_RAW permission granted on Linux (and likely MacOS X, which also uses BSD socket library). My network library, which I had put up elsewhere on here some 12 years ago or so, did provide raw sockets, besides TCP and UDP and had VIs implementing the ICMP echo command, but that was of very limited value because of these permission issues. The command line ping utility on Linux is THE way to solve it there.

It's unfortunate, but with raw sockets someone really could easily start doing very nasty things, accidentally or on purpose, so I understand why they are protected.

Also note that many servers nowadays disable the ICMP Echo command on purpose to avoid attempts to be DOS attacked.

Edited by Rolf Kalbermatter
Link to comment

I should test on my embedded cDAQ again.  Because looking at the code it executes this command on Linux:

for i in {%s} ;do (ping $i -c 1 -w 1  >/dev/null && echo "$i" &) ;done

With a code comment from myself saying 1s was the smallest amount I could use, maybe it does support smaller values.

Link to comment
7 hours ago, viSci said:

Thanks for all the replies!  I tried the ICMP approach courtesy of https://forums.ni.com/t5/LabVIEW/A-list-of-IP/td-p/4174564 but still cannot seem to get the timeout to work for values <500ms.

IcmpSendEcho_Ping.llb 75.29 kB · 0 downloads

Depending on what you mean by doesn't work and "do not respect"; Problems with timeouts <500ms sounds like NAGLE to me.

Link to comment
11 hours ago, ShaunR said:

Depending on what you mean by doesn't work and "do not respect"; Problems with timeouts <500ms sounds like NAGLE to me.

Would seem strange. Nagle is an algorithm on the TCP/IP protocol level, not on the IP level. More likely a limitation of the synchronous nature of that API. Using the asynchronous IcmpSendEcho2() might give more control. However it is more complex as you have to either use a Windows event handle or a callback routine, with the second being not really a feasible option in the LabVIEW Call Library Node.

Link to comment

I have found that disabling the Nagle algorithm (using Visa Property TCP NoDelay) greatly improved the responsiveness of my tcpip communications and alleviated intermittent lockup between the raw socket ping and my TCPIP transactions. 

Edited by viSci
Link to comment
On 4/2/2022 at 9:22 PM, viSci said:

So far, the best solution I have found is based on the Raw Socket Ping library where I can consistently achieve 50ms pings.  Thanks Rolf!

https://forums.ni.com/t5/LabVIEW/Ping-Ping-Ping-Native-Win2k-LV7-code/m-p/708672?view=by_date_ascending#M324556

That API doesn't work for me out-of-the-box. Fails with a nebulous error at "select".  IcmpSendEcho works fine, however.

Link to comment
19 hours ago, ShaunR said:

That API doesn't work for me out-of-the-box. Fails with a nebulous error at "select".  IcmpSendEcho works fine, however.

I have tried and failed to use the iphelper api in the past. Can you tell me how to actually get hold of the source code for it. You google it and you get a load of old articles about asking for the Microsoft SDK on a CD, broken links or information that didn't make a lot of sense to me. 

 

Probably just my inexperience with C++ development though.

Link to comment
21 hours ago, ShaunR said:

That API doesn't work for me out-of-the-box. Fails with a nebulous error at "select".  IcmpSendEcho works fine, however.

Did you start your program as Administrator? Raw sockets is a privileged resource on all modern OSes, only available to especially elevated processes such as being started as root/admin or under Linux with an explicit privilege for the process.

IcmpSendEcho() supposedly calls into a device driver (or maybe service) to do the actual work. Since they execute in the context of the Windows kernel, they are not restricted by those pesky user right limitations. By default they execute with System privileges which is almost like an Administrator and in certain ways even more.

Edited by Rolf Kalbermatter
Link to comment
2 hours ago, Rolf Kalbermatter said:

Did you start your program as Administrator?

I ran the LabVIEW IDE as administrator ("run as") even though I am already a local administrator. That usually sorts those issues out (certainly worked for creating symlinks, back in the day), Select returned error -1 and the "WSA get Last error" (and "get last error")  reported "0" but that's probably to be expected due to thread dependent error stacks.

I vaguely remember having similar problems when not using

FD_ZERO(&ReadSock);
FD_SET(Socket, &ReadSock);
FD_ZERO(&WriteSock);
FD_SET(Socket, &WriteSock);

properly before "select" when getting the socket status for normal sockets-which may or may not be relevant. Those are just macro's though so can't be called.

Link to comment
On 4/5/2022 at 12:58 PM, ShaunR said:

I ran the LabVIEW IDE as administrator ("run as") even though I am already a local administrator. That usually sorts those issues out (certainly worked for creating symlinks, back in the day), Select returned error -1 and the "WSA get Last error" (and "get last error")  reported "0" but that's probably to be expected due to thread dependent error stacks.

I vaguely remember having similar problems when not using

FD_ZERO(&ReadSock);
FD_SET(Socket, &ReadSock);
FD_ZERO(&WriteSock);
FD_SET(Socket, &WriteSock);

properly before "select" when getting the socket status for normal sockets-which may or may not be relevant. Those are just macro's though so can't be called.

That library was reworked by me in 2008, before LabVIEW had any 64-bit version. Unfortunately, SOCKET in Windows is an UINT_PTR which means that it is a 32-bit integer on 32-bit LabVIEW and 64-bit integer on 64-bit LabVIEW. But you can NOT port this over to Linux as is. There the file-descriptor used for all socket functions is an explicit int, so always a 32-bit value! This is because the Berkeley socket library is originally developed for Unix and build around the generic socket concept in Unix which traditionally uses int file descriptors. When Microsoft took the Berkeley socket library and adapted it for Windows as WinSock library, it was already very revolutionary to have this as a fully 32-bit code library, Windows itself was still mainly a selector based 16-bit environment. And Microsoft likes to use handles, which are opaque pointers and happened to be 32-bit integers too back then.

The library as posted on the NI forum does NOT work in 64-bit Windows, and that is not just because of the 64-bit SOCKET handle itself but also because of the fd_set data structure which is defined as follows:

typedef struct fd_set {
        u_int   fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

Now on 32-bit LabVIEW there is nothing strange here. The fd_array with SOCKET handles begins at offset 4, so the LabVIEW array of 2 * 32-bit integers works perfectly fine. The first array element corresponds to fd_count and the second element to the first element in the fd_array and if fd_count doesn't contain more than 1 as value it does not matter that the fd_array doesn't contain all 64 elements that this structure is declared for.

On 64-bit the SOCKET is a 64-bit integer and is naturally aligned on an 8 byte boundary in that structure. So there is a dummy 32-bit filler element between fd_count and fd_array. And the first SOCKET should be a 64-bit integer.

Solution to make this work on 64-bit LabVIEW, besides changing the SOCKET handle parameter for all functions to be a pointer sized integer, is to use a conditional compile structure. For the 32-bit case use an array of 32-bit integers as fd_set just as is used now, for the 64-bit case you need to use an array of 64-bit integers. The rest remains the same. You even can use an array of 2 * 64-bit integers, just the same as for the 32-bit case with an array of 32-bit integers. Since we run on a Little Endian machine under Windows, the lower significant 32-bits of the first 64-bit element happen to match the location in memory where the fd_count is expected, the extra 4 filler bytes are overwritten by the higher significant 32-bits of the first 64-bit array element which is not a problem. They are really DON'T CARE.

Your observation that this weird error can happen if the fd_set structure is not correctly initialized was spot on. But not because it is not really correctly initialized in the VI (it is) but because the array of 32-bit values has a different layout than what the 64-bit Winsock library expects.

Edited by Rolf Kalbermatter
  • Thanks 1
Link to comment
1 hour ago, Rolf Kalbermatter said:

That library was reworked by me in 2008, before LabVIEW had any 64-bit version. Unfortunately, SOCKET in Windows is an UINT_PTR which means that it is a 32-bit integer on 32-bit LabVIEW and 64-bit integer on 64-bit LabVIEW. But you can NOT port this over to Linux as is. There the file-descriptor used for all socket functions is an explicit int, so always a 32-bit value! This is because the Berkeley socket library is originally developed for Unix and build around the generic socket concept in Unix which traditionally uses int file descriptors. When Microsoft took the Berkeley socket library and adapted it for Windows as WinSock library, it was already very revolutionary to have this as a fully 32-bit code library, Windows itself was still mainly a selector based 16-bit environment. And Microsoft likes to use handles, which are opaque pointers and happened to be 32-bit integers too back then.

The library as posted on the NI forum does NOT work in 64-bit Windows, and that is not just because of the 64-bit SOCKET handle itself but also because of the fd_set data structure which is defined as follows:

typedef struct fd_set {
        u_int   fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

Now on 32-bit LabVIEW there is nothing strange here. The fd_array with SOCKET handles begins at offset 4, so the LabVIEW array of 2 * 32-bit integers works perfectly fine. The first array element corresponds to fd_count and the second element to the first element in the fd_array and if fd_count doesn't contain more than 1 as value it does not matter that the fd_array doesn't contain all 64 elements that this structure is declared for.

On 64-bit the SOCKET is a 64-bit integer and is naturally aligned on an 8 byte boundary in that structure. So there is a dummy 32-bit filler element between fd_count and fd_array. And the first SOCKET should be a 64-bit integer.

Solution to make this work on 64-bit LabVIEW, besides changing the SOCKET handle parameter for all functions to be a pointer sized integer, is to use a conditional compile structure. For the 32-bit case use an array of 32-bit integers as fd_set just as is used now, for the 64-bit case you need to use an array of 64-bit integers. The rest remains the same. You even can use an array of 2 * 64-bit integers, just the same as for the 32-bit case with an array of 32-bit integers. Since we run on a Little Endian machine under Windows, the lower significant 32-bits of the first 64-bit element happen to match the location in memory where the fd_count is expected, the extra 4 filler bytes are overwritten by the higher significant 32-bits of the first 64-bit array element which is not a problem. They are really DON'T CARE.

Your observation that this weird error can happen if the fd_set structure is not correctly initialized was spot on. But not because it is not really correctly initialized in the VI (it is) but because the array of 32-bit values has a different layout than what the 64-bit Winsock library expects.

Nice and great explanation :)

I changed the read node's readfds, writefd and exceptfds to be arrays of unsigned pointer sized integers (instead of U32 ) which should do the same from your explanation (and it's quick 'n easy ;)). It now works a treat :)

From all the aggro I've had trying to get even the tiniest morsel of information out of Linux people (present company excepted, of course). I'll let them worry about it working over there.

Edited by ShaunR
Link to comment
10 hours ago, ShaunR said:

Nice and great explanation :)

I changed the read node's readfds, writefd and exceptfds to be arrays of unsigned pointer sized integers (instead of U32 ) which should do the same from your explanation (and it's quick 'n easy ;)). It now works a treat :)

From all the aggro I've had trying to get even the tiniest morsel of information out of Linux people (present company excepted, of course). I'll let them worry about it working over there.

Smart thinking, I was overthinking this with the conditional compile structure. Just create a 64-bit array and declare the parameter as pointer sized integer array and LabVIEW will happily do the conversion automatically on 32-bit platform.

Just one caveat although it is of no significance nowadays anymore. This trick of treating the fd_set structure simply as an array of integers only works on Little Endian with 8-byte compiler alignment. On Big Endian this would go wrong as the count stays a 32-bit integer no matter what and the low significant 32-bit of the first 64-bit integer would go in the wrong place.

But the only Big Endian platform that LabVIEW still had until recently was the VxWorks real-time targets and their Berkeley socket implementation is mostly Unix like although with some interesting deviations, that usually would not really matter if you just compile a program from C sources with the correct headers but definitely could go very bad when trying to interface with a LabVIEW VI to it that has no notation of their header files but simply requires you to play header parser yourself.

Edited by Rolf Kalbermatter
  • Thanks 1
Link to comment
2 hours ago, Rolf Kalbermatter said:

Just create a 64-bit array

I don't think you even need to do that. The way LV deals with 32 and 64 bit is commutative.

2 hours ago, Rolf Kalbermatter said:

But the only Big Endian platform that LabVIEW still had until recently was the VxWorks real-time targets

Oh god. don't talk to me about VxWorks. I still have nightmares about that from SQLite. It's what things like this were for:

static void byteReverse(unsigned char *buf, unsigned longs)
{

  static int littleEndian = -1;
  if (littleEndian < 0)
  {
    /* Are we little or big endian? This method is from Harbison & Steele. */
    union
    {
      long l;
      char c[sizeof(long)];
    } u;
    u.l = 1;
    littleEndian = (u.c[0] == 1) ? 1 : 0;
  }

  if (littleEndian != 1)
  {
    unsigned int t;
    do
    {
      t = (unsigned int) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
          ((unsigned) buf[1] << 8 | buf[0]);
      *(unsigned int *) buf = t;
      buf += 4;
    }
    while (--longs);
  }
}

Good riddance, IMO :)

Link to comment
41 minutes ago, ShaunR said:

I don't think you even need to do that. The way LV deals with 32 and 64 bit is commutative.

I think you should. The upper 32-bits of the SOCKET on 64-bit platforms may not be used nowadays but there is no guarantee that it won't be used in the future, for instance as you get Windows 12 or Windows Jubilee or something, that will only be available as 64-bit OS anyways. Someone at Microsoft may decide to stick something into those unused 32-bits of the SOCKET or may decide that it should be directly a pointer anyways. Then this library won't work if you didn't consequently change the socket handle to be a 64-bit integer in LabVIEW and all Call Library Nodes to use a pointer sized integer for these parameters.

As to byte swapping, it can be an annoyance but I find that sort of byte twiddling actually entertaining. 😀

Edited by Rolf Kalbermatter
Link to comment
2 hours ago, Rolf Kalbermatter said:

I think you should. The upper 32-bits of the SOCKET on 64-bit platforms may not be used nowadays but there is no guarantee that it won't be used in the future, for instance as you get Windows 12 or Windows Jubilee or something, that will only be available as 64-bit OS anyways. Someone at Microsoft may decide to stick something into those unused 32-bits of the SOCKET or may decide that it should be directly a pointer anyways.

Well. It's a choice of the original developer. Personally I don't try to second guess M$ and if it falls over after an upgrade, I'll charge to fix it :) You never know, they might be using 128 bit or quantum machines by then so it may be for nowt. We found a problem and came up with a solution. Our work here is done :)

Edited by ShaunR
Link to comment
19 hours ago, ShaunR said:

Well. It's a choice of the original developer. Personally I don't try to second guess M$ and if it falls over after an upgrade, I'll charge to fix it :) You never know, they might be using 128 bit or quantum machines by then so it may be for nowt. We found a problem and came up with a solution. Our work here is done :)

What I mean here is that SOCKET is defined to be an UINT_PTR. This means on 64-bit Windows it is a 64-bit entity even nowadays. The fact that the upper 32-bit may or may not be used internally is an implementation detail that we as API users should NOT rely on.  So while it MAY work to only transport 32-bit around in the LabVIEW library, this may be something that only works by change on your machine, or may sometime in the future fail generally because of some under the hood design change. While I agree that you could argue that such future changes are not your current problem and you are justified to charge in the future for that modification if it starts to fail, it is the fact that it may even nowadays fail. We don't know what a SOCKET is and even by looking at its numeric value and finding that it never goes higher than a few 1000, indicating that it is really more like an index into some internal table, there is no guarantee for that. It could very well be a pointer that happens to usually be mapped to a below 4GB address but may suddenly also be allocated above that limit and then your 32-bit handling fails. 

Factually, treating the SOCKET variable always as 32-bit value at even a single point in your program is a bug as it does not match the published API documentation. The fact that it may not crash or fail for you, is no argument to leave that bug in the code. It's the same thing generally with people who dabble with the Call Library Node: "Look mom, it doesn't crash anymore!!", wrap it up and declare victory is the totally wrong mindset here. Once it doesn't always crash immediately anymore, the real hard work only starts, as not every memory corruption immediately will crash your process, but instead may crash way down the timeline after you have written and added several dozen more modules to your program and then the bug hunting is VERY cumbersome as you have no idea where to start and may instinctively suspect the last changes you made (well I usually have an idea, if there is a DLL involved anywhere it has about a 95% chance to be the culprit even if it never crashed before 😀). The only exception to that rule was for me in the past if it was a LabVIEW binding that came with NI drivers. Those are usually very stable and almost never the cause of a crash. But any other DLL binding, self developed or from any 3rd party is a prime suspect in those cases.

Edited by Rolf Kalbermatter
Link to comment
1 hour ago, Rolf Kalbermatter said:

Factually, treating the SOCKET variable always as 32-bit value at even a single point in your program is a bug as it does not match the published API documentation. The fact that it may not crash or fail for you, is no argument to leave that bug in the code. It's the same thing generally with people who dabble with the Call Library Node: "Look mom, it doesn't crash anymore!!", wrap it up and declare victory is the totally wrong mindset here.

That's not what's happening here. The correct call is made in the CLFN (I love the [unsigned] pointer sized integer :) ) it's just that I'm using LabVIEW coercion to upscale a  32 bit array to 64 bit array in 64 bit machines where you are arguing to use LabVIEW coercion to downscale  a 64 bit array to a 32 bit array in 32 bit machines. At present, it makes no difference .

The only valid (but very weak) argument here is whether M$ may use the upper bits of a U64 which means, if they do, then the upper bits may no longer be zero's so you may have to change values in the array anyway (so can change it to 64 bit then) and it will no longer be able to be used on 32 bit machines. Or, like I said, they go to 128 bit or something (and keep it the upper bits unused) when your solution would be "as buggy" as my solution but both would still work. I don't have a crystal ball so will cross that bridge when I come to it.

Link to comment
6 hours ago, ShaunR said:

That's not what's happening here. The correct call is made in the CLFN (I love the [unsigned] pointer sized integer :) ) it's just that I'm using LabVIEW coercion to upscale a  32 bit array to 64 bit array in 64 bit machines where you are arguing to use LabVIEW coercion to downscale  a 64 bit array to a 32 bit array in 32 bit machines. At present, it makes no difference .

The only valid (but very weak) argument here is whether M$ may use the upper bits of a U64 which means, if they do, then the upper bits may no longer be zero's so you may have to change values in the array anyway (so can change it to 64 bit then) and it will no longer be able to be used on 32 bit machines.

Actually that is wrong. It will still work on 32-bit LabVIEW. The 32-bit Windows Winsock is documented to use 32-bit SOCKET values, LabVIEW will pass a 32-bit value to the DLL even if the LabVIEW wire is 64-bit as you configured it as pointer sized variable in the Call Library Node. This means all is peachy in both 32-bit and 64-bit LabVIEW no matter what Windows decides to do underneath. With your current setup the 64-bit value returned by socket(..) will be downconverted to 32-bit and transported as such through all VIs and then upconverted each time to a 64-bit value when calling a WinSock library function (if that parameter is correctly configured as pointer sized integer. If not only the lower 32-bit for that parameter are guaranteed to be initialized and the upper 32-bit may contain garbage, which may or may not be a problem). That may go fine as long as the upper 32-bits aren't used but it may also go wrong. If you change it to be a LabVIEW 64-bit integer everywhere in LabVIEW itself and configure those parameters everywhere as pointer sized integer, it will always go right, until Windows 128-bit comes out of course, but I have a suspicion that by that time, we both will not program in LabVIEW anymore, and LabVIEW may be an anecdotal remark in the history of programming languages that existed somewhere in a long ago past.

Edited by Rolf Kalbermatter
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.