你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 编程语言
利用NetBIOS进行Windons网络编程
 

  :本文介绍了NetBIOS编程的一些基本概念,并通过一个异步事件服务器和一个异步事件客户机的例子,详细说明了NetBIOS进行Windows编程的基本方法。

关键词NetBIOS, 异步事件,服务器,客户机

                                                                                         

    “网络基本输入/输出系统”(Network Basic Input/Output SystemNetBIOS)是1983年由Sytex公司为IBM公司开发的一种标准应用程序编程接口,并被微软采用。1985年,IBM改进了NetBIOS,推出了NetBIOS扩展用户接口(NetBIOS Extended User Interface,NetBEUI)通信协议,它占用内存少,配置简单,适用于小型局域网不同计算机之间的通信,但不具有跨网段工作的能力,不支持路由机制。NetBIOS是一种与“协议无关”的编程接口,它使应用程序不用理解网络细节,应用程序可通过TCP/IPNetBEUISPX/IPX运行。下面我们介绍以下NetBIOS编程用到的一些重要概念及其实现方法。

一、理解NetBIOS

1、 LANA编号

理解LAN适配器(LAN Adapter,LANA)编号是NetBIOS进行网络编程的关键

所在。网络的传输协议是通过LANA编号同NetBIOS对应起来,每个LANA编号对应于网卡及传输协议的唯一组合。因此,我们在编程时要注意,两台要进行通信计算机必须至少安装有同一种协议,并且这两台计算机通信所依赖的LANA编号对应的网络协议要相同,否则即使这两台计算机安装相同的协议也无法进行通信。LANA编号范围在09之间,其中,LANA 0代表默认的LANA

2、 NetBIOS名字

NetBIOS名字可分为两种类型:唯一名字(Unique Name)和组名(Group Name)。

顾名思义,唯一名字只允许一台计算机注册该名字,一旦唯一名字注册成功,其他计算机如果再注册该名字,就会出现:“名字重复”的错误,微软网络中的机器名采用的就是NetBIOS唯一名字。组名则是一组计算机的总称,可以用来接收发给这一组计算机的数据。值得注意的是:组名可以和唯一名字同名,这会引起发送或接收数据的目的出现错误!NetBIOS名字长度为16个字符,其中第16个字符用于区分不同的网络服务。关于计算机注册NetBIOS名字的信息可以利用Nbtstat命令查看。

3、 NetBIOS提供的服务

NetBIOS提供两种服务:面向连接的服务和数据报服务(无连接)。

面向连接的服务为两台需要进行通信的计算机建立一个连接,并利用错误探测和恢复机制保证数据在通信的两端准确无误的传输,它适于传输比较长的消息。对于NetBIOS,服务器在对想通过它建立通信的LANA编号上注册,而对于位于其他计算机上的客户机会搜索服务器注册的名字,并将它解析为机器名,然后发出进行通信的请求。

数据报服务是无连接的,因而它不能保证数据有序、正确的传输,但它可以节省建立连接的开销,它适合短消息的传输。在NetBIOS中,客户机只是将发送数据的目的地定义为服务器注册的进程名,而不进行任何连接。

二、NetBIOS编程的实现

NetBIOS的所有函数声明、常数都在头文件“Nb30.h”中定义,在编程时还须与Netapi32.lib库进行链接。NetBIOS接口通过一个函数实现:

UCHAR Netbios (PNCB pNCB)

其中,参数pNCB指向一个网络控制块(Net Control Block,NCB)指针,NCB结构如下:

typedef struct _NCB {

         UCHAR   ncb_command   // NetBIOS命令

            UCHAR   ncb_retcode;     // 指定操作的返回代码         

   UCHAR   ncb_lsn;         // 本地会话编号       

   UCHAR   ncb_num;         // 本地名字编号        

            PUCHAR  ncb_buffer;      // 数据缓冲区地址        

            WORD    ncb_length;      // 缓冲区长度       

            UCHAR   ncb_callname[NCBNAMSZ]; // 远程应用程序名

            UCHAR   ncb_name[NCBNAMSZ];  // 本地应用程序名   

            UCHAR   ncb_rto;         // 接收操作延时

            UCHAR   ncb_sto;         // 发送操作延时        

void (CALLBACK *ncb_post)( struct _NCB * );

                    // 异步命令完成后需调用的后例程地址

            UCHAR   ncb_lana_num;    // LANA 编号

            UCHAR   ncb_cmd_cplt;    // 指定操作的返回代码      

            UCHAR   ncb_reserve[10]; // 保留字段    

            HANDLE  ncb_event;       // Win32事件句柄     

} NCB, *PNCB;

此外,编程时应注意调用NetBIOS函数的同步和异步问题。NetBIOS命令调用本身均为同步,即在完成指定命令之前,会一直调用NetBIOS模块。而在实际编程时,我们通常需要进行异步调用,即希望允许多个客户机同时与服务器进行连接,这就需要让NetBIOS命令与异步标志逻辑或(OR)操作,但必须在ncb_post字段中指定一个后例程,或在ncb_event字段中指定一个事件句柄。

下面,我以实现一个异步事件服务器和一个异步事件客户机为例,具体说明NetBIOS的编程实现,其中,服务器接收由客户机发送的数据。

 

1、 异步事件服务器的实现

首先,我们进行初始化工作,列举可用的LANA编号,并重设:

    if (LanaEnum(&lenum) != NRC_GOODRET)

        return 1;

    if (ResetAll(&lenum, (UCHAR)MAX_SESSIONS, (UCHAR)MAX_NAMES,

            FALSE) != NRC_GOODRET)

        return 1;

&lenum是一个LANA_ENUM结构变量,其定义如下:

typedef  struct  LANA_ENUM

{

      UCHAR      length

      UCHAR      lana[MAX_LANA+1]

}  LANA_ENUM, *PLANA_ENUM

其中,length指出本地计算机可用的LANA的数量,lana表示由这些LANA编号组成的一个数组。

然后为每个LANA编号分配NCB结构,并添加服务器名字,执行异步侦听。如果有客户机与服务器连接成功,则服务器接收由客户机发送的数据。程序代码如下:

    // 为异步事件分配一组句柄

    EventArray = (HANDLE*)GlobalAlloc(GMEM_FIXED,

            sizeof(HANDLE) * lenum.length);

    // 为每个LANA编号分配一个NCB结构

    GlobalClients = (PNCB)GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT,

            sizeof(NCB) * lenum.length);

 

    // 产生事件,为LANA编号添加服务器名,并执行异步侦听

    for(i = 0; i < lenum.length; i++)

    {

        EventArray[i] = GlobalClients[i].ncb_event = CreateEvent(NULL, TRUE, FALSE, NULL);

        AddName(lenum.lana[i], SERVER_NAME, &num);

        Listen(&GlobalClients[i], lenum.lana[i], SERVER_NAME);

    }

   // 此时若在windows下运行Nbtstat–n 服务器名,会看到一个NetBIOS

   // 名字表。

   // 产生事件,为LANA编号添加服务器名,并执行异步侦听

    while (1)

    {       

        // 等待,直到有一个连接建立   

        RetEvent = WaitForMultipleObjects(lenum.length, EventArray, FALSE, INFINITE);

        if (RetEvent == WAIT_FAILED)

        {

            printf("等待连接失败!\n");

           break;

        }

 

        // 遍历每个NCB结构,是否有多个连接建立,

        // 如果ncb_cmb_plt的值不是NRC_PENDING,即连接成功,

        // 则产生接收数据的是线程,并为它创建一个新的NCB结构。

        for(i = 0; i < lenum.length; i++)

        {

            if (GlobalClients[i].ncb_cmd_cplt != NRC_PENDING)

            {

                pncb = (PNCB)GlobalAlloc(GMEM_FIXED, sizeof(NCB));

                memcpy(pncb, &GlobalClients[i], sizeof(NCB));

                pncb->ncb_event = 0;

 

                hThread = CreateThread(NULL, 0, ClientThread,

                    (LPVOID)pncb, 0, &ThreadId);

                CloseHandle(hThread);

                // 重设事件句柄,进行另一个侦听

                ResetEvent(EventArray[i]);

                Listen(&GlobalClients[i], lenum.lana[i], SERVER_NAME);

            }

        }

    }

最后,关闭所有句柄,释放内存空间。

    for(i = 0; i < lenum.length; i++)

    {

        DelName(lenum.lana[i], SERVER_NAME);

        CloseHandle(EventArray[i]);

    }

    GlobalFree(GlobalClients);

 

2、 异步事件客户机的实现

同实现服务器一样先进行初始化。在给LANA编号添加客户机名字之后,进行异步连接,若连接成功,则向服务器发送数据,最后,关闭句柄,释放内存空间。这里我只给出异步连接和发送数据的部分程序代码:

    // 产生一个事件,并把它分配给一个相应的NCB结构,并执行异步连接

    for(i = 0; i < lenum.length; i++)

    {

        EventArray[i] = CreateEvent(NULL, TRUE, FALSE, NULL);

        pncb[i].ncb_event = EventArray[i];

        AddName(lenum.lana[i], ClientName, &Num);

        Connect(&pncb[i], lenum.lana[i], ServerName, ClientName);

    }

 

    // 等待,直到至少有一个连接成功

    RetEvent = WaitForMultipleObjects(lenum.length, EventArray, FALSE,

        INFINITE);

    if (RetEvent == WAIT_FAILED)

    {

        ErrorCode.Format("等待连接失败!");

        AfxMessageBox(ErrorCode);

    }

    else

    {

        // 如果有多个连接成功,关闭多余的连接,我们只用由

        // WaitForMultipleObjects函数返回的连接;如果没有连接成功,

        // 则取消。

        for(i = 0; i < lenum.length; i++)

        {

            if (i != RetEvent)

            {

                if (pncb[i].ncb_cmd_cplt == NRC_PENDING)

                    Cancel(&pncb[i]);

                else

                    Hangup(pncb[i].ncb_lana_num, pncb[i].ncb_lsn);

            }

        }

       

        // 发送消息

        sprintf(SendBuffer, m_sendmsg);

        RetValue = Send(pncb[RetEvent].ncb_lana_num,

            pncb[RetEvent].ncb_lsn, SendBuffer,

             strlen(SendBuffer));

        if (RetValue != NRC_GOODRET)

            AfxMessageBox("无法建立连接!");

        Hangup(pncb[RetEvent].ncb_lana_num, pncb[RetEvent].ncb_lsn);

    }

 

    本程序在Windows98环境下,由VC++6.0编译通过。

参考文献:

1、  Anthony Jones, Jim Ohlund,  Network Programming for Microsoft

Windows,1999

2、MSDN Library

 

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089