-
Network Status
Im not sure if this is meant to be in the networking section, if so, sorry.
It uses the win api and its more of a windows question in general anyway.
Im trying to find out the status of all computers connected on my LAN, i want to find out if they are online/offline and if online, i want to find out if they are logged in (if possible).
So far, i managed to find some code that lists all computer names on a network, and using this list, i used somewhat of a hack to check if they were online/offline (pinging them and hiding the command prompt).
Code:
void NetworkStatus()
{
//vars
do
{
pbuf = NULL;
statusNetAPI = NetServerEnum(NULL, 101, &pbuf, 25600, &entriesread, &totalentries, SV_TYPE_ALL, NULL, &resume);
if ((statusNetAPI == NERR_Success || statusNetAPI == ERROR_MORE_DATA) && pbuf != NULL)
{
pSI = (SERVER_INFO_101 *)pbuf;
for (i = 0; i < entriesread; ++i)
{
sprintf(sBuffer, "%S: ", pSI[i].sv101_name);
strcat(sReturn, sBuffer);
sprintf(sBuffer, "ping %S -n 1 -w 100", nBegin);
nStart = GetTickCount();
LoadProgram(sBuffer); //just loads 'sBuffer' and hides the window (async)
if (GetTickCount() - nStart >= 50)
//Offline
else
//Online
}
}
if (pbuf != NULL)
NetApiBufferFree(pbuf);
}
while (statusNetAPI == ERROR_MORE_DATA);
}
but the problem is, this code takes about 100 ms per computer, and i got around 30 computers on my LAN.. if im lucky, it takes 3 seconds, but usually, it takes anywhere between 1 to 10 minutes.. also, its not that accurate, sometimes it returns offline for a computer thats definately online.
-
What happens if you comment out the LoadProgram call? This should give you an idea whether the culprit is NetServerEnum or LoadProgram. Could you cache the computer list? You could look into IcmpSendEcho or IcmpSendEcho2 to replace the ping call. These take an IP address so you would have to use gethostbyname first. Does this network have a domain controller and is the code running on it?
>> its not that accurate, sometimes it returns offline for a computer thats definately online. <<
If a few threads are active and/or code or data needs to be loaded from the disk the ping call could easily take more than 50ms.
-
i dont think it has a domain controller, and im just running it from 1 of the clients.. netserverenum isnt the issue at the moment, its the ping, im looking into icmpSendEcho(2) now. thanks for the advice
edit:
is pinging the only way to check if a computer is online/offline? because the ping timeout is kinda slow, and if i set it too low, its inaccurate
-
You can use IcmpSendEcho2 to send out all your ping requests at the same time. Here is a sample demonstrating IcmpSendEcho2:
Code:
#include <winsock2.h>
#include <windows.h>
#include <Ipexport.h>
#include <icmpapi.h>
#include <stdio.h>
#if defined(_MSC_VER)
#pragma comment(lib, "Iphlpapi.lib")
#pragma comment(lib, "Ws2_32.lib")
#endif
/*
* Simple structure of arrays to create Targets collection.
*/
#define MAX_TARGETS 300
struct
{
struct in_addr IPAddresses[MAX_TARGETS];
BYTE Buffers [MAX_TARGETS][sizeof(ICMP_ECHO_REPLY) + 32];
HANDLE Events [MAX_TARGETS];
UINT cTargets;
} Targets;
/*
* Add an IP address to the Targets collection.
*/
BOOL new_Target(LPCSTR szIPAddress)
{
if (Targets.cTargets < MAX_TARGETS)
{
Targets.Events[Targets.cTargets] = CreateEvent(NULL, FALSE, FALSE, NULL);
Targets.IPAddresses[Targets.cTargets].S_un.S_addr = inet_addr(szIPAddress);
Targets.cTargets++;
return TRUE;
}
return FALSE;
}
/*
* There appears to be a problem with the import lib for IcmpParseReplies
* so we load it at run-time.
*/
DWORD wrapIcmpParseReplies(LPVOID ReplyBuffer, DWORD ReplySize)
{
typedef (WINAPI * pIPR)(LPVOID, DWORD);
static pIPR dl_IcmpParseReplies;
if (!dl_IcmpParseReplies)
{
HMODULE hICMP = LoadLibrary(TEXT("ICMP.dll"));
dl_IcmpParseReplies = (pIPR) GetProcAddress(hICMP, "IcmpParseReplies");
}
if (dl_IcmpParseReplies)
return dl_IcmpParseReplies(ReplyBuffer, ReplySize);
else
return 0;
}
/*
* main function.
*/
int main(void)
{
size_t i;
DWORD ret;
HANDLE hIcmp;
BYTE PingData[16];
/* Add IP Addresses to check. */
new_Target( "66.139.79.229" );
new_Target( "192.168.0.1" );
new_Target( "192.168.0.2" );
new_Target( "192.168.0.17" );
new_Target( "10.0.0.1" );
new_Target( "10.0.0.2" );
/* You can also check your entire subnet with something like: */
for (i = 1;i < 256;i++)
{
char buf[50];
sprintf(buf, "192.168.0.%d", i);
new_Target(buf);
}
hIcmp = IcmpCreateFile();
/* Send out all our async ping requests. Timeout is 250ms. Change if needed. */
for (i = 0; i < Targets.cTargets; i++)
{
IcmpSendEcho2(hIcmp, Targets.Events[i], NULL, 0,
Targets.IPAddresses[i].S_un.S_addr, PingData, sizeof(PingData),
NULL, Targets.Buffers[i], sizeof(Targets.Buffers[i]), 250);
}
/* Wait for all our ping requests to return or timeout. */
WaitForMultipleObjects(Targets.cTargets, Targets.Events, TRUE, 2500);
/* Check the results. */
for (i = 0; i < Targets.cTargets; i++)
{
ret = wrapIcmpParseReplies(Targets.Buffers[i], sizeof(Targets.Buffers[i]));
printf("%s: %s\n", ret != 0 ? "UP " : "DOWN", inet_ntoa(Targets.IPAddresses[i]));
}
IcmpCloseHandle(hIcmp);
getchar();
return 0;
}
Notes:
- I successfully used this code to check my subnet. Run-time was under a second.
- If you have slower paths in your network (WAN, VPN, etc) you may need to increase the timeout.
- Smart network components (routers, switches, etc) may detect this as a DOS attack and reject the ping packets.
- Error checking and cleanup code is incomplete.
- You may find it faster to get rid of NetServerEnum. You could ping the entire subnet and then lookup names with gethostbyaddr.
-
that works perfectly, thanks again anonytmouse :)