如何快速枚举局域网内所有存活主机IP

我想快速搜索所有主机,我已经试过以下方法,注意,我强调的是快速
1.用ping,当然可以,但是如果要在程序中实现不大合理,而且我觉得速度也快不到哪里去
2.用winpcap发送arp请求包可以获得,但是获取时间不确定,而且获取速度不算太快
3.用SendARP函数,对于需要扫描255个主机时简直太慢了不能忍受,感觉程序都无法响应了。于是我尝试着?55个线程分别调用SendARP函数,结果发现大多数SendArp都调用失败,错误值是31。不知道为什么
4.看到网上有人使用先枚举网络资源,再获取主机信息,然后有主机名获取IP,最后由IP获取MAC的方法,我觉得速度也快不到哪里去(这个没有具体试过)

—————————————————————

方式  4,  采用网上邻居的方法,但是只能枚举本工作组内的主机

源码:

[php]DWORD  dwScope  =  RESOURCE_CONTEXT;
NETRESOURCE  *NetResource  =  NULL;
HANDLE  hEnum;
WNetOpenEnum(  dwScope,  NULL,  NULL,
NULL,  &hEnum  );
if  (  hEnum  )
{
DWORD  Count  =  0xFFFFFFFF;
DWORD  BufferSize  =  2048;
LPVOID  Buffer  =  new  char[2048];
WNetEnumResource(  hEnum,  &Count,
Buffer,  &BufferSize  );
NetResource  =  (NETRESOURCE*)Buffer;

char  szHostName[200];
unsigned  int  i;

for  (  i  =  0;i  <  BufferSize/sizeof(NETRESOURCE);i++,  NetResource++  )
{
if  (  NetResource->dwUsage  ==RESOURCEUSAGE_CONTAINER  &&NetResource->dwType  ==RESOURCETYPE_ANY  )
{
if  (  NetResource->lpRemoteName  )
{
CString  strFullName  =NetResource->lpRemoteName;
if  (  0  ==strFullName.Left(2).Compare("\\\\")  )
strFullName  =strFullName.Right(strFullName.GetLength()-2);
gethostname(  szHostName,strlen(  szHostName  )  );
host  =  gethostbyname(strFullName);
if(host  ==  NULL)  continue;
strTemp.Format("%s",strFullName);
m_List.InsertItem(i,strTemp,0);                  //  获得地址添加到列表
}
}
}
delete  Buffer;
WNetCloseEnum(  hEnum  );  [/php]

—————————————————————

第四种方法,网上邻居也是采用netenum  api

————————————————————–

这两天看到一个《基于UDP协议的网段扫描器》,

代码如下,可以得到工作组,机器名,用户名和MAC地址

[php]BYTE  bs[50]={0x0,0x00,0x0,0x10,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x43,0x4b,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x0,0x0,0x21,0x0,0x1};
HANDLE  wait_handle;

m_UDPSocket.SendTo((void*)bs,50,destPORT,m_strIP,0);//向指定的ip发数据报
WaitForSingleObject(
wait_handle,                //  等待事件的句柄
nWait      //  超时
);
ResetEvent(wait_handle);//将事件重新置回非触发状态

void  CNBTSTATDlg::OnReceive()
{
BYTE  Buf[500];
CString  str,strIP,strHost,strHex,strMac,Host,Group,User;
UINT  dport;
m_UDPSocket.ReceiveFrom(Buf,500,strIP,dport,0);//接收数据
//如果接收到的ip为空或者与原来接收到的ip相同,则返回
if(strIP==(char)NULL  |  |strIP==strOldIP)return;
strOldIP=strIP;
int  index=m_ListView.InsertItem(0,strIP);//将ip插入ListView
strHost="";  //机器名字
strHex="";//MAC地址
User="?";//
Host="\\";
int  tem=0,num=0;
bool  bAdd=true;
//根据数据报规则取出相应的信息
for(i=57;i<500;i++)  //57-72
{
if(Buf[i]==0xcc)break;
if(Buf[i]==0x20)bAdd=false;
if(bAdd)
{
str.Format("%c",Buf[i]);
if(Buf[i]>=’  ‘)strHost+=str;

str.Format("%02x.",Buf[i]);
strHex+=str;
}

if((++tem)%18==0)
{
bAdd=true;
strHost.TrimRight((char)NULL);
if(strHost=="")
{
strMac.Delete(17,strMac.GetLength()-17);
m_ListView.SetItem(index,4,LVIF_TEXT,strMac,  0,  0,  0,0);
break;
}

if(num==0&&strHost!="")
{
m_ListView.SetItem(index,2,LVIF_TEXT,strHost,  0,  0,  0,0);
Host=strHost;
num++;
}
else
{
if(Host!=strHost&&num==1&&strHost!="")
{
m_ListView.SetItem(index,1,LVIF_TEXT,strHost,  0,  0,  0,0);
Group=strHost;
num++;
}
else
{
if(strHost!=Host&&strHost!=Group&&num==2&&strHost!="")
{
User=strHost;
if(User!="__MSBROWSE__")
{
m_ListView.SetItem(index,3,LVIF_TEXT,User,  0,  0,  0,0);
num++;
}
}
}

}

strMac=strHex;
strHost="";
strHex="";

}

}
//触发事件,导致线程函数的继续执行
SetEvent(wait_handle);

}  [/php]

—————————————————————

采用多线程调用SendARP的方法不错,效率很高(我的局域网11台机器在线,几秒钟就搞定:))不用每开一个线程就Sleep(),这样速度很慢,可以预先定义一个CString  szIp[255];数组,作为255个线程的参数,下面是程序的大概:

[php]//全局变量
typedef  struct  ipmac
{
char  szIp[16];
char  szMac[6];

}IPMAC;
IPMAC  host[255];
int  k=0;
CRITICAL_SECTION  cs;
/*线程函数,用来查询IP对应的MAC地址*/
DWORD  WINAPI  ArpThread(LPVOID  lParam)
{
char  *  szIp=(char  *)lParam;
ULONG  pMac[2];
ULONG  pulen=6;
int  ret;
TRACE(szIp);
if  ((ret=SendARP(inet_addr(szIp),0,pMac,&pulen))==0)
{
EnterCriticalSection(&cs);  //多线程同步,呵呵:0
strcpy(host[k].szIp,szIp);
PBYTE  pbyte=(PBYTE)pMac;
for  (int  i=0;i<5;i++)
{
host[k].szMac[i]=pbyte[i];
TRACE("%02X-",pbyte[i]);
}
TRACE("%02X",pbyte[5]);
host[k].szMac[5]=pbyte[5];
k++;
LeaveCriticalSection(&cs);

}
else
{
TRACE("SendARP  Error  %d",ret);
}
return  0;
}

/*枚举局域网内所有主机,并将IP/MAC对插入ListBox中显示*/
void  CTestArpDlg::OnButton1()
{
//  TODO:  Add  your  control  notification  handler  code  here
HANDLE  hthread[254];
CString  IpSuffix="192.168.10.";
CString  strIp[254];
InitializeCriticalSection(&cs);
for  (int  i=0;i<254;i++)
{
strIp[i].Format("%d",i+1);
strIp[i]=IpSuffix+strIp[i];
hthread[i]=CreateThread(NULL,0,ArpThread,strIp[i].GetBuffer(0),0,NULL);

}
/*呵呵,因为一次只能等待  64个内核对象,所以只有分几次了*/
/*当然也可以用循环了*/
WaitForMultipleObjects(64,hthread,TRUE,INFINITE);
WaitForMultipleObjects(64,&hthread[64],TRUE,INFINITE);
WaitForMultipleObjects(64,&hthread[128],TRUE,INFINITE);
WaitForMultipleObjects(62,&hthread[192],TRUE,INFINITE);
DeleteCriticalSection(&cs);
CString  temp;
for  (i=0;i<k;i++) ?<br="">           {
PBYTE  pmac=(PBYTE)host[i].szMac;
temp.Format("%s(%02x-%02x-%02x-%02x-%02x-%02x)",host[i].szIp,pmac[0],pmac[1],pmac[2],pmac[3],pmac[4],pmac[5]);
m_list.AddString(temp);
}

}
[/php]

文 / Aex
LEAVE A REPLY
loading