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

我想快速搜索所有主机,我已经试过以下方法,注意,我强调的是快速  
1.用ping,当然可以,但是如果要在程序中实现不大合理,而且我觉得速度也快不到哪里去  
2.用winpcap发送arp请求包可以获得,但是获取时间不确定,而且获取速度不算太快  
3.用SendARP函数,对于需要扫描255个主机时简直太慢了不能忍受,感觉程序都无法响应了。于是我尝试着?55个线程分别调用SendARP函数,结果发现大多数SendArp都调用失败,错误值是31。不知道为什么  
4.看到网上有人使用先枚举网络资源,再获取主机信息,然后有主机名获取IP,最后由IP获取MAC的方法,我觉得速度也快不到哪里去(这个没有具体试过)  
 
---------------------------------------------------------------  
 
方式  4,  采用网上邻居的方法,但是只能枚举本工作组内的主机  
 
源码:  
 
       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  );  
 
---------------------------------------------------------------  
 
第四种方法,网上邻居也是采用netenum  api,codeproject上有个例子  
www.codeproject.com/treectrl/pathpicker.asp  
 
--------------------------------------------------------------  
 
 
这两天看到一个《基于UDP协议的网段扫描器》,大致代码如下,可以得到工作组,机器名,用户名和MAC地址  
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);  
             
 
}  
 
---------------------------------------------------------------  
 
采用多线程调用SendARP的方法不错,效率很高(我的局域网11台机器在线,几秒钟就搞定:))不用每开一个线程就Sleep(),这样速度很慢,可以预先定义一个CString  szIp[255];数组,作为255个线程的参数,下面是程序的大概:  
//全局变量  
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           {  
                       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);  
           }  
             
 
}
posted @ 2009-10-25 01:26  装配中的脑袋  阅读(2301)  评论(0编辑  收藏  举报