Delphi完成NetBIOS廣播收發(fā)
發(fā)表時(shí)間:2024-02-17 來(lái)源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]NetBIOS網(wǎng)絡(luò)協(xié)議對(duì)于很多讀者來(lái)說(shuō)可能比較陌生,但其實(shí)它是由IBM開(kāi)發(fā)的一個(gè)很古老的協(xié)議,當(dāng)年在LAN上也風(fēng)光一時(shí)。說(shuō)它老,其實(shí)也不過(guò)10年光景,IT業(yè)的發(fā)展實(shí)在是太快。由于NetBIOS不具備路由功能,也就是說(shuō)它的數(shù)據(jù)包無(wú)法跨網(wǎng)段傳輸,因此在廣域網(wǎng)、城域網(wǎng)大行其道的今天,它已退居配角。如果你...
NetBIOS網(wǎng)絡(luò)協(xié)議對(duì)于很多讀者來(lái)說(shuō)可能比較陌生,但其實(shí)它是由IBM開(kāi)發(fā)的一個(gè)很古老的協(xié)議,當(dāng)年在LAN上也風(fēng)光一時(shí)。說(shuō)它老,其實(shí)也不過(guò)10年光景,IT業(yè)的發(fā)展實(shí)在是太快。由于NetBIOS不具備路由功能,也就是說(shuō)它的數(shù)據(jù)包無(wú)法跨網(wǎng)段傳輸,因此在廣域網(wǎng)、城域網(wǎng)大行其道的今天,它已退居配角。如果你有心的話,能夠發(fā)現(xiàn)在Window95 / 98的網(wǎng)絡(luò)協(xié)議中仍然保留著NetBIOS,不過(guò)它已經(jīng)改名叫NetBEUI(NetBIOS擴(kuò)展用戶接口),是NetBIOS的Microsoft改進(jìn)版。另外在TCP/IP以及IPX/SPX協(xié)議中,也依然保留了對(duì)NetBIOS的支持,只要查看網(wǎng)絡(luò)協(xié)議屬性中的高級(jí),就能看到啟用NetBIOS的選項(xiàng)。
之所以這樣是有原因的。NetBIOS協(xié)議短小精悍,非常適用于小型局域網(wǎng),特別是一些對(duì)實(shí)時(shí)性要求較高的網(wǎng)絡(luò)環(huán)境。NetBIOS的廣播功能由于有開(kāi)發(fā)使用方便、系統(tǒng)開(kāi)銷小的優(yōu)點(diǎn),所以在很多場(chǎng)合仍然被大量使用。筆者由于工作需要,在一個(gè)航天測(cè)控軟件的編制中就使用了NetBIOS廣播功能。
我原以為這是件很簡(jiǎn)單的工作,因?yàn)閃IN32API中提供了一個(gè)Netbios函數(shù),里面封裝了所有函數(shù)和數(shù)據(jù)結(jié)構(gòu),用起來(lái)很方便,在BC和VC下都如此?墒怯捎谶@次是使用流行的Delphi作編譯器,卻遇到了意想不到的麻煩:號(hào)稱全面移植WIN32API的Delphi中偏偏沒(méi)有Netbios函數(shù)!這下頓時(shí)讓我方寸大亂。怎么辦?總不能從底層干起吧?而且時(shí)間也不允許。在冷靜下來(lái)之后,我忽然想到,既然WIN95支持NetBIOS,那么系統(tǒng)就一定會(huì)提供DLL支持,編譯器本身是沒(méi)有底層支持的。于是我在機(jī)器中搜索,果然,在SYSTEM目錄下有一個(gè)Netbios.dll,用快速查看將其打開(kāi),在導(dǎo)出表部分顯示如下:
導(dǎo)出表:
序數(shù) 入口 名稱
0000 00001a37 NetbiosAddthd
0001 000019eb NetbiosDelete
0002 00001a96 NetbiosDelthd
0003 000019b1 NetbiosInitialize
0004 0000186b PostRoutineCaller
0005 0000102e _Netbios
注意到那個(gè)0005號(hào)_Netbios導(dǎo)出函數(shù)了嗎?那就是我需要的!經(jīng)過(guò)緊張的試驗(yàn)調(diào)試,證明它和WIN32API手冊(cè)上的Netbios完全一樣。剩下的工作就比較簡(jiǎn)單了,定義一個(gè)NCB(Netbios控制塊)記錄,將NCB數(shù)據(jù)結(jié)構(gòu)封裝在里面;聲明一個(gè)后處理例程以及消息處理過(guò)程,以完成廣播數(shù)據(jù)的接收和發(fā)送。有關(guān)NCB數(shù)據(jù)結(jié)構(gòu)的詳細(xì)內(nèi)容以及NetBIOS廣播的原理,限于篇幅我就省略了。需要的朋友可以查看BC或VC的Help或相關(guān)書籍。下面是有關(guān)的Delphi源代碼。
/////////Netbios單元///////////
unit netbios;
interface
uses windows,messages,F(xiàn)orms,SysUtils;
type
{$X+}{$A+}
file://聲明一個(gè)NCB記錄指針。
PNCB=^NCB;
file://聲明一個(gè)后處理例程的過(guò)程類型。
POST=procedure(var ncbR:PNCB);
file://以下是NCB記錄,教訓(xùn)1:將上面的編譯選項(xiàng)置為{$A+}以取消數(shù)據(jù)對(duì)齊。如果在廣播中有浮點(diǎn)數(shù)的話,數(shù)據(jù)對(duì)齊會(huì)讓你大吃苦頭!我已經(jīng)有過(guò)慘痛教訓(xùn)!:(
NCB=record
ncb_command:UCHAR;
ncb_retcode:UCHAR;
ncb_lsn:UCHAR;
ncb_num:UCHAR;
ncb_buffer:PCHAR;
ncb_length:WORD;
ncb_callname:array [1..16] of UCHAR;
ncb_name:array [1..16] of UCHAR;
ncb_rto:UCHAR;
ncb_sto:UCHAR;
ncb_post:POST;
ncb_lana_num:UCHAR;
ncb_cmd_cplt:UCHAR;
ncb_reserve:array [1..10] of UCHAR;
ncb_event:HANDLE;
end;
file://聲明自己的Netbios函數(shù)。教訓(xùn)2:一定要使用pascal調(diào)用規(guī)范,否則,嘿嘿!!
function NetbiosSR(ncbX:PNCB):UCHAR;pascal;
file://初始化NCB。
procedure InitNCB(var ncbY:PNCB);
file://后處理例程,注意使用遠(yuǎn)指針。
procedure postrout(var ncbR:PNCB);stdcall;far;
var
char_buffer:array[0..511]of UCHAR;
int_buffer:array[1..512]of Byte;
implementation
file://調(diào)用系統(tǒng)的Netbios。dll中的Netbios函數(shù)標(biāo)號(hào)是6。Delphi搜索外部文件的順序是當(dāng)前目錄→系統(tǒng)目錄→其他目錄,別忘了保證存在Netbios.dll。
function NetbiosSR(ncbX:PNCB):UCHAR;external
‘netbios'' index 6;
procedure InitNCB(var ncbY:PNCB);
var
x:integer;
begin
ncbY.ncb_command:=0;
ncbY.ncb_retcode:=0;
ncbY.ncb_lsn:=0;
ncbY.ncb_num:=0;
ncbY.ncb_length:=512; file://數(shù)據(jù)緩沖長(zhǎng)度,最大512B。
for x:=1 to 16 do
begin
ncbY.ncb_callname[x]:=0;
ncbY.ncb_name[x]:=0;
end;
ncbY.ncb_rto:=0;
ncbY.ncb_sto:=0;
ncbY.ncb_lana_num:=0;
ncbY.ncb_cmd_cplt:=0;
for x:=1 to 10 do
ncbY.ncb_reserve[x]:=0;
ncbY.ncb_event:=0;
end;
file://后處理例程的作用是當(dāng)接收到廣播消息時(shí),立即向相應(yīng)窗口發(fā)送消息。我在這里偷了點(diǎn)懶,以廣播方式發(fā)送一個(gè)定時(shí)器消息。如果你愿意可以向指定窗口發(fā)送自定義消息,這樣要復(fù)雜一些。
首先,要把指定窗口的句柄傳遞給后臺(tái)處理例程。通常這是做不到的,但可以利用一些技巧做到。在NCB記錄后面緊挨著聲明一個(gè)句柄類型,然后把指定窗口的句柄賦值給它的實(shí)例變量;這樣句柄變量的地址與NCB是連續(xù)的。在后處理中通過(guò)指針或匯編語(yǔ)句將ncbR的地址移到最后一個(gè)字節(jié)+1,就是窗口句柄的起始地址。明白嗎?至于自定義消息,需要重新編譯連接庫(kù),限于篇幅我就不羅嗦了,有興趣的可以自己嘗試。
procedure postrout(var ncbR:PNCB);
begin
sendMessage(wnd_BROADCAST,WM_TIMER,0,0);
end;
end.
////////窗口單元//////////
unit broadcast;
interface
uses
Windows,Messages,SysUtils,Classes,Graphics,Controls,F(xiàn)orms,Dialogs,netbios;
type
Tmain=class(TForm)
private
{Private declarations}
file://消息處理過(guò)程,注意消息宏要與后處理中的一致。
procedure post_main(var Message:TMessage);message WM_TIMER;
public
{Public declarations}
end;
var
main: Tmain;
ncbname:UCHAR;
ncbRock:PNCB;
post_add:POST;
implementation
{$R *.DFM}{$A-}{$I-}
/////////主窗口建立過(guò)程/////////
procedure Tmain.FormCreate(Sender: TObject);
var
ret:UCHAR;
i,x,y:integer;
p:single;
begin
new(ncbRock);
randomize();i:=0;
FillChar(char_buffer,sizeof(char_buffer),0);
post_add:=@postrout;
file://取后處理例程的地址。
ncbRock.ncb_buffer:=@char_buffer; file://取數(shù)據(jù)緩沖區(qū)的地址。
InitNCB(ncbRock);
ret:=9;
ncbname:=random(100);
ncbRock.ncb_name[1]:=ncbname;
ncbRock.ncb_command:=$30;
file://加名,ret為0加名成功。
while ((i<10)and(ret<>0)) do
begin
ret:=netbiosSR(ncbRock);
i:=i+1;
end;
if ret<>0 then
begin
for i:=1 to 20 do
messagebeep(-1);
MessageDlg(‘網(wǎng)絡(luò)通信無(wú)法實(shí)現(xiàn)!您需要關(guān)閉程序重新運(yùn)行.'',mtWarning,
[mbOk],0);
end
else if ret=0 then
begin
ncbRock.ncb_post:=post_add;
ncbRock.ncb_command:=$a3; file://異步接收方式字。
ncbRock.ncb_event:=0;
ncbRock.ncb_length:=512;
ret:=netbiosSR(ncbRock);
end;