发信人: 2001asp(ujj) 
整理人: delfan(2001-04-12 09:44:22), 站内信件
 | 
 
 
8
 
 
   NetBIOS网络协议对于很多读者来说可能比较陌生,但其实它是由IBM开发的一个很古老的协议,当年在LAN上也风光一时。说它老,其实也不过10年光景,IT业的发展实在是太快。由于NetBIOS不具备路由功能,也就是说它的数据包无法跨网段传输,因此在广域网、城域网大行其道的今天,它已退居配角。如果你有心的话,能够发现在Window95 / 98的网络协议中仍然保留着NetBIOS,不过它已经改名叫NetBEUI(NetBIOS扩展用户接口),是NetBIOS的Microsoft改进版。另外在TCP/IP以及IPX/SPX协议中,也依然保留了对NetBIOS的支持,只要查看网络协议属性中的高级,就能看到启用NetBIOS的选项。
 
   之所以这样是有原因的。NetBIOS协议短小精悍,非常适用于小型局域网,特别是一些对实时性要求较高的网络环境。NetBIOS的广播功能由于有开发使用方便、系统开销小的优点,所以在很多场合仍然被大量使用。笔者由于工作需要,在一个航天测控软件的编制中就使用了NetBIOS广播功能。
 
   我原以为这是件很简单的工作,因为WIN32API中提供了一个Netbios函数,里面封装了所有函数和数据结构,用起来很方便,在BC和VC下都如此。可是由于这次是使用流行的Delphi作编译器,却遇到了意想不到的麻烦:号称全面移植WIN32API的Delphi中偏偏没有Netbios函数!这下顿时让我方寸大乱。怎么办?总不能从底层干起吧?而且时间也不允许。在冷静下来之后,我忽然想到,既然WIN95支持NetBIOS,那么系统就一定会提供DLL支持,编译器本身是没有底层支持的。于是我在机器中搜索,果然,在SYSTEM目录下有一个Netbios.dll,用快速查看将其打开,在导出表部分显示如下:
 
   导出表:
 
 序数 入口 名称 
 0000 00001a37 NetbiosAddthd 
 0001 000019eb NetbiosDelete 
 0002  00001a96 NetbiosDelthd 
 0003 000019b1 NetbiosInitialize 
 0004  0000186b PostRoutineCaller 
 0005 0000102e _Netbios 
 
          
    注意到那个0005号_Netbios导出函数了吗?那就是我需要的!经过紧张的试验调试,证明它和WIN32API手册上的Netbios完全一样。剩下的工作就比较简单了,定义一个NCB(Netbios控制块)记录,将NCB数据结构封装在里面;声明一个后处理例程以及消息处理过程,以完成广播数据的接收和发送。有关NCB数据结构的详细内容以及NetBIOS广播的原理,限于篇幅我就省略了。需要的朋友可以查看BC或VC的Help或相关书籍。下面是有关的Delphi源代码。
 
 
   /////////Netbios单元///////////
 
   unit netbios;
 
   interface
 
 
    uses windows,messages,Forms,SysUtils;
 
     type
 
      {$X+}{$A+}
 
       file://声明一个NCB记录指针。
 
 
       PNCB=^NCB;
 
      file://声明一个后处理例程的过程类型。
 
       POST=procedure(var ncbR:PNCB);
 
      file://以下是NCB记录,教训1:将上面的编译选项置为{$A+}以取消数据对齐。如果在广播中有浮点数的话,数据对齐会让你大吃苦头!我已经有过惨痛教训!:(
 
       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函数。教训2:一定要使用pascal调用规范,否则,嘿嘿!!
 
      function NetbiosSR(ncbX:PNCB):UCHAR;pascal;
 
      file://初始化NCB。
 
       procedure InitNCB(var ncbY:PNCB);
 
      file://后处理例程,注意使用远指针。
 
       procedure postrout(var ncbR:PNCB);stdcall;far;
 
        var
 
         char_buffer:array[0..511]of UCHAR;
 
         int_buffer:array[1..512]of Byte;
 
        implementation
 
         file://调用系统的Netbios。dll中的Netbios函数标号是6。Delphi搜索外部文件的顺序是当前目录→系统目录→其他目录,别忘了保证存在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://数据缓冲长度,最大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://后处理例程的作用是当接收到广播消息时,立即向相应窗口发送消息。我在这里偷了点懒,以广播方式发送一个定时器消息。如果你愿意可以向指定窗口发送自定义消息,这样要复杂一些。
 
   首先,要把指定窗口的句柄传递给后台处理例程。通常这是做不到的,但可以利用一些技巧做到。在NCB记录后面紧挨着声明一个句柄类型,然后把指定窗口的句柄赋值给它的实例变量;这样句柄变量的地址与NCB是连续的。在后处理中通过指针或汇编语句将ncbR的地址移到最后一个字节+1,就是窗口句柄的起始地址。明白吗?至于自定义消息,需要重新编译连接库,限于篇幅我就不罗嗦了,有兴趣的可以自己尝试。
 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,Forms,Dialogs,netbios;
 
    type
 
      Tmain=class(TForm)
 
     private
 
      {Private declarations}
 
      file://消息处理过程,注意消息宏要与后处理中的一致。
 
      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-}
 
   /////////主窗口建立过程/////////
 
     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://取数据缓冲区的地址。
 
       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(‘网络通信无法实现!您需要关闭程序重新运行.'',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;
 
       end;
 
      ///////////广播消息处理过程/////
 
      procedure Tmain.post_main(var Message:TMessage);
 
       var
 
        x:integer;
 
        ret:UCHAR;
 
       begin
 
         file://取出数据缓冲区的内容
 
         for x:=0 to 511 do
 
          int_buffer[x+1]:=char_buffer[x];
 
          ////以下可以进行数据处理////
 
          file://重新打开异步接受。
 
          ncbRock.ncb_post:=post_add;
 
          ncbRock.ncb_command:=$a3;
 
          ncbRock.ncb_event:=0;
 
          ncbRock.ncb_length:=512;
 
          ret:=netbiosSR(ncbRock);
 
         end;
 
       end.
 
   注:广播发送非常简单,不再详述。上述程序经过一年运行完全可靠。另外,经过改造可以将其改为LAN下的聊天程序。 
 
 
  ---- E网情深国立华侨大学商检95本主页(http://sj95.126.com)
 
           | 
 
 
 |