我想快速搜索所有主机,我已经试过以下方法,注意,我强调的是快速
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);
}
}