全部博文(788)
分类:
2008-08-19 15:55:42
不能用 每隔一段时间ping一次网络内所有电脑,因为这样消耗太大,而且有些电脑是禁止了ping的
如果客户端不装任何东西的话,挺难
本来服务器遍历网络内所有机器(网上邻居)应该可以实现,不过如果你要实时知道的话,就很难了,总不能服务器不干别的,就不停的遍历吧
楼主的要求太高,俗话说鱼和熊掌不能兼得,
真的超难
看你的网络结构了,如果服务器也是路由器,或者代理,那么去读取服务器的ARP Table即可.
如果服务器不是路由和代理,那么就嗅探ARP包了.
如果网络是交换式的,只有循环发送ARP包或者ping,或者TCP ping.
啥子超难哦。结构很简单,线程+网络扫描。 只是实现的代码比较长而已。
unit LanScan;
interface
uses
Windows, Messages, SysUtils, Forms, Classes, WinSock;
const
NBTPort = 137; //设定对端UDP端口号
UDPPort = 8327; //设定本端UDP端口号
WM_SOCK = WM_USER + $0001; //自定义windows消息
Over_IP = 'Over'; //组件退出时的标志
NbtstatPacket: array[0..49]of Byte =($0,$0,$0,$0,$0,$1,$0,$0,$0,$0,
$0,$0,$20,$43,$4b,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$41,$41,$41,$41,
$41,$41,$41,$41,$41,$41,$0,$0,$21,$0,$1);
type
TNbt = class;
TSendThread = class(TThread)
private
{ Private declarations }
FNbt : TNbt;
FIP : string; //当前探测的IP地址
FEvent : THandle; //延迟事件句柄
protected
{ protected declarations }
procedure SendData; //发送数据
procedure NilThread; //设置线程结束标识
procedure Execute; override;
procedure SetEvents(const nIP: string); //取消延迟
public
{ public declarations }
ID : integer; //线程实例标识
constructor Create(AOwner: TNbt);
end;
TOnBegin = procedure (const nIPNumber: integer) of object;
TOnEnd = procedure (const nTotalScan: integer) of object;
TOnProcess = procedure (const nHasDone: integer) of object;
TOnStatus = procedure (const nMsg: string) of object;
//扫描状态
TOnReceive = procedure (const nIP, nHostName, nUserName,
nGroupName, nMacAddr: string) of object;
TNBT = class(TComponent)
private
{ Private declarations }
FBusy : boolean; //正在扫描
FEndIP, //结束IP
FBeginIP : string; //开始IP
FTimeOut : integer; //超时间隔
FIPList : TStrings; //开始到结束
FLock : TRTLCriticalSection; //临界区变量
FData : array [0..49] of byte; //NbtstatPacket
FHasDone : integer; //已扫描个数
FOnStatus : TOnStatus; //扫描状态
FOnReceive: TOnReceive; //数据解析
FOnBegin : TOnBegin; //扫描开始
FOnEnd : TOnEnd; //扫描结束
FOnProcess: TOnProcess; //扫描过程
FHandle : HWnd; //消息处理使用
FSock : TSocket; //套节字
FAddr : TSockAddr;
FSockAddrIn : TSockAddrIn;
FThreadNum : integer; //线程个数
FThreads : array of TSendThread; //扫描线程
protected
{ protected declarations }
function GetIPList: boolean;
procedure EnterCS; //进入临界区
procedure LeaveCS; //离开临界区
procedure SendData(const nIP:string); //发送数据
procedure ReadData(var nMessage: TMessage); //消息处理
procedure SetEvents(const nIP: string); //取消延迟
procedure SetThreadNum(const nNum: integer);
procedure RecvNbMsg(nBuf: array of byte; nLen: integer; const nIP: string);
public
{ public declarations }
constructor Create(AOwner: TComponent); override; //创建
destructor Destroy; override; //销毁
procedure StartScan; //开始扫描
procedure StopScan; //停止扫描
procedure FreeThreads; //释放线程
procedure NilThread(const nID: integer); //设置线程结束标识
published
{ published declarations }
property EndIP : string read FEndIP write FEndIP;
property BeginIP : string read FBeginIP write FBeginIP;
property TimeOut : integer read FTimeOut write FTimeOut;
property ThreadNum: integer read FThreadNum write SetThreadNum;
property OnStatus: TOnStatus read FOnStatus write FOnStatus;
property OnReceive: TOnReceive read FOnReceive write FOnReceive;
property OnEnd : TOnEnd read FOnEnd write FOnEnd;
property OnBegin : TOnBegin read FOnBegin write FOnBegin;
property OnProcess: TOnProcess read FOnProcess write FOnProcess;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyUse', [TNBT]);
end;
//Name: IsLegalIP
//Param: nIP,待测试IP
//Return: 若nIP合法返回真
function IsLegalIP(const nIP: string): boolean;
begin
if inet_addr(pchar(nIP))=INADDR_NONE then
Result := false
else Result := True;
end;
{************************ TSendThread ************************}
constructor TSendThread.Create(AOwner: TNbt);
begin
inherited Create(True);
FNbt := AOwner;
FreeOnTerminate := True;
FEvent := CreateEvent(nil, True, False, nil);
end;
procedure TSendThread.SendData;
begin
FNbt.SendData(FIP);
end;
procedure TSendThread.NilThread;
begin
FNbt.NilThread(ID);
end;
procedure TSendThread.SetEvents(const nIP: string);
begin
if (nIP=FIP) or (nIP=Over_IP) then SetEvent(FEvent);
end;
procedure TSendThread.Execute;
begin
while not Terminated do
begin
FIP := ''; FNbt.EnterCS;
if FNbt.FIPList.Count = 0 then
begin
FNbt.LeaveCS;
Break;
end;
FIP := FNbt.FIPList[0];
FNbt.FIPList.Delete(0);
FNbt.LeaveCS;
Synchronize(SendData);
WaitForSingleObject(FEvent, FNbt.FTimeOut);
ResetEvent(FEvent);
end;
CloseHandle(FEvent);
Synchronize(NilThread);
end;
{************************* TNBT ***************************}
constructor TNBT.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FTimeOut := 100;
FBusy := False;
FEndIP := '127.0.0.1';
FBeginIP := '127.0.0.1';
FThreadNum:= 3;
end;
destructor TNBT.Destroy;
begin
StopScan;
inherited Destroy;
end;
procedure TNBT.EnterCS;
begin
EnterCriticalSection(FLock);
end;
procedure TNBT.LeaveCS;
begin
LeaveCriticalSection(FLock);
end;
procedure TNBT.SetThreadNum(const nNum: integer);
begin
if (nNum > 5) or (nNum < 1) then
raise Exception.Create('线程个数最好在1-5之间');
FThreadNum := nNum;
end;
procedure TNBT.NilThread(const nID: integer);
var i: integer;
begin
for i:= Low(FThreads) to High(FThreads) do
if Assigned(FThreads[i]) and (FThreads[i].ID = nID) then
begin
FThreads[i] := nil;
Break;
end;
for i:= Low(FThreads) to High(FThreads) do
if Assigned(FThreads[i]) then Exit;
StopScan;
end;
procedure TNBT.FreeThreads;
var i: integer;
begin
for i:= Low(FThreads) to High(FThreads) do
begin
if not Assigned(FThreads[i]) then Continue;
FThreads[i].Terminate;
FThreads[i].SetEvents(Over_IP);
end;
SetLength(FThreads,0);
end;
procedure TNBT.StartScan;
var i : integer;
nWSAData: TWSAData;
begin
if FBusy then exit;
FHasDone := 0;
FIPList := TStringList.Create;
if not GetIPList then
begin
FIPList.Free;
Exit;
end;
FHandle := AllocateHWnd(ReadData);
InitializeCriticalSection(FLock);
if WSAStartup($101, nWSAData)=1 then
Exception.Create('WinSock初始化失败');
FSock := Socket(AF_INET, SOCK_DGRAM, 0);
if (FSock = INVALID_SOCKET) then
begin
CloseSocket(FSock);
Exception.Create('Socket创建失败');
end;
FAddr.sin_family := AF_INET;
FAddr.sin_addr.S_addr := INADDR_ANY;
FAddr.sin_port := htons(UDPPORT);
if Bind(FSock, FAddr, sizeof(FAddr)) <> 0 then
begin
CloseSocket(FSock);
Exception.Create('WinSock绑定失败');
end;
WSAAsyncSelect(FSock, FHandle, WM_SOCK, FD_READ);
FillChar(FSockAddrIn, SizeOf(FSockAddrIn), #0);
FSockAddrIn.SIn_Family := AF_INET;
FSockAddrIn.SIn_Port := htons(NBTPORT);
for i:=0 to 49 do FData[i] := NbtstatPacket[i];
SetLength(FThreads, FThreadNum);
for i:=Low(FThreads) to High(FThreads) do
begin
FThreads[i] := TSendThread.Create(self);
FThreads[i].ID := i;
Fthreads[i].Resume;
end;
FBusy := True;
if Assigned(FOnBegin) then FOnBegin(FIPList.Count);
end;
procedure TNBT.StopScan;
begin
if FBusy then
begin
FreeThreads;
FIPList.Free;
WSACleanup();
DeallocateHWnd(FHandle);
DeleteCriticalSection(FLock);
FBusy := False;
end;
if not (csDestroying in ComponentState)
and Assigned(FOnEnd) then FOnEnd(FHasDone);
end;
function TNBT.GetIPList: boolean;
var i: integer;
nIP: string;
nIP1,nIP2: dWord;
begin
Result := False;
if not (IsLegalIP(FEndIP) and IsLegalIP(FBeginIP)) then exit;
nIP1 := ntohl(inet_addr(pchar(FBeginIP)));
nIP2 := ntohl(inet_addr(pchar(FEndIP)));
for i := nIP1 to nIP2 do
begin
//去掉x.x.x.0或x.x.x.255的地址。
if (((i - 255) mod 256)=0)or((i mod 256)=0) then continue;
nIP := inet_ntoa(in_addr(htonl(i)));
FIPList.Add(nIP);
end;
Result := True;
end;
procedure TNBT.ReadData(var nMessage: TMessage);
var nIP:string;
nEvent: word;
nLen1,nLen2: integer;
nBuf: array [1..500] of byte;
begin
if nMessage.msg <> WM_SOCK then exit;
nLen1 := SizeOf(FSockAddrIn);
nEvent := WSAGetSelectEvent(nMessage.LParam);
if nEvent = FD_READ then
begin
nLen2 := recvfrom(FSock, nBuf, sizeof(nBuf), 0, FSockAddrIn, nLen1);
if nLen2 > 0 then
begin
with FSockAddrIn.sin_addr.S_un_b do
nIP:=format('%d.%d.%d.%d',[ord(s_b1),ord(s_b2),ord(s_b3),ord(s_b4)]);
RecvNbMsg(nBuf, nLen2, nIP);
end;
SetEvents(nIP);
end;
end;
procedure TNBT.RecvNbMsg(nBuf: array of byte; nLen: integer; const nIP: string);
var i,j,nPos,nCount: integer;
sStr,
nHostName, nUserName,
nGroupName, nMacAddr: string;
begin
nCount := 0;
for i:=1 to nlen do
begin
if((nBuf[i]=$21) and (nBuf[i+1]=$00) and (nBuf[i+2]=$01)) then
begin
nCount := nBuf[i+9];
break;
end;
end;
if nCount = 0 then exit;
sStr := '';
nPos := i + 10;
for i := nPos to (nPos + 18*nCount - 1) do
begin
if (((i - nPos) mod 18) =0) then
begin
for j:=0 to 14 do
begin
if Trim(Char(nBuf[i+j])) = '' then nBuf[i+j] := Ord(' ');
sStr := sStr + Char(nBuf[i+j]);
end;
if (nBuf[i+16] and $80)=$80 then
begin
if nBuf[i+15]=$0 then nGroupName := Trim(sStr);
end else
begin
if nBuf[i+15]=$3 then nUserName := Trim(sStr)
else
if nBuf[i+15]=$20 then nHostName := Trim(sStr);
end;
sStr :='';
end;
end;
for i:=0 to 5 do
sStr := sStr + Format('%.2x.',[nBuf[i+nPos+18*nCount]]);
Delete(sStr, Length(sStr), 1);
nMacAddr := Trim(sStr);
if Assigned(FOnReceive) then
FOnReceive(nIP,nHostName,nUserName,nGroupName,nMacAddr);
end;
procedure TNBT.SendData(const nIP: string);
var nLen : integer;
begin
FSockAddrIn.SIn_Addr.S_addr := inet_addr(pchar(nIP));
nLen := SendTo(FSock, FData[0],50, 0, FSockAddrIn, sizeof(FSockAddrIn));
if Assigned(FOnStatus) then
begin
if nLen <> 50 then
FOnStatus('数据没有发送完毕') else
if nLen = SOCKET_ERROR then
FOnStatus('WinSock错误,发送失败')
else FOnStatus('正在扫描,主机: ' + nIP);
end;
Inc(FHasDone);
if Assigned(FOnProcess) then FOnProcess(FHasDone);
end;
procedure TNBT.SetEvents(const nIP: string);
var i: integer;
begin
for i:=Low(FThreads) to High(FThreads) do
if Assigned(FThreads[i]) then FThreads[i].SetEvents(nIP);
end;
end.
新建一个package把这个上边的pas文件添加到里边安装后有一个组件nbt
////
procedure TForm1.cmdStartScanClick(Sender: TObject);
begin
self.Nbt1.BeginIP:='192.168.168.1';
self.Nbt1.EndIP:='192.168.168.253';
self.Nbt1.StartScan;
end;
procedure TForm1.Nbt1Receive(const nIP, nHostName, nUserName, nGroupName,
nMacAddr: String);
begin
self.ListBox1.Items.Add( nip+'=='+nMacAddr );
end;
procedure TForm1.cmdStopScanClick(Sender: TObject);
begin
self.Nbt1.StopScan;
end;
sanmaotuo(老冯) 可能没理解我的意思
按你说的那样确实很简单(如果这样能解决就不叫超难问题了),但是,不停的扫描网络是不现实的。而且要把网内所有电脑都扫描一遍是不现实的。
你想想看就比如这个局域网是192.168.x.x开头的机器,那么要全部扫描一次需要每次扫描65025台电脑,如果每次扫描0.5秒钟作为TimeOut,也就是9个小时才能完成一次扫描。 等你扫描完,天都亮了
我在考虑是否有局域网专用的方法
化杯粪喂力量 的ARP方法有创意,我先去试验一下,然后告诉大家结果
谢谢 失踪的月亮,我试试看扫描速度
邻居发现机制把,。
由他附近的邻居举报,这是以太网的原理
邻居发现机制是怎么做的? 怎么编写代码?能否给点提示
由于intranet有个特点,广播机制
当一个网卡启用,或者一个pc查找另外一台需要的机器,就要发arp包广播,这个广播在网卡底层是任何pc机器都可以接受到的,只是在应用层如果不需要的话将把它抛弃
简单说
一台机器接入网络时,将发送一个arp广播,告诉所有的人,我来了。这也是如果同一个网络设置到两个相同的 ip地址系统马上就要报冲突的原因哈
程序实现 要用到ndis编程,接受所有的arp包,解包,就ok了
是个,楼上的说得对啊。
我一个题目就想到了广播,怎么会被认为是超级难题。
说是容易,有没有代码参考一下?
如何实现基于web的指定数据提取?急~~~~~~~~请各位高手指教!
有没有代码参考一下?
采用域管理,就可以实现你的要求。
看来实现还是有难度
原理就如上面说的一样
编程有很多方法实现
可以自己写 tdi 驱动,或者利用winpcap (windows环境下)提供的api ,这样就可以抓取arp包了
自己去查 下winpcap 编程代码,网上很多,就不重复叙述了
偶学习啦.
"一台机器接入网络时,将发送一个arp广播,底层的问题."
以上回答都不是我想要的。勉强结贴。