当前位置:K88软件开发文章中心编程语言C/C++C/C++01 → 文章内容

利用NetBIOS进行Windows网络编程

减小字体 增大字体 作者:佚名  来源:翔宇亭IT乐园  发布时间:2019-1-3 0:07:16

:2011-03-16 16:35:38

本文介绍了NetBIOS编程的一些基本概念,并通过一个异步事件服务器和一个异步事件客户机的例子,详细说明了NetBIOS进行Windows编程的基本方法。文中涉及的程序在Windows98环境下,由VC++6.0编译通过。

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

一、理解NetBIOS

1、 LANA编号

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

2、 NetBIOS名字

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

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; // NetBIOSming令
            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 * );

    // 异步ming令完成后需调用的后例程地址 
            UCHAR ncb_lana_num; // LANA 编号
            UCHAR ncb_cmd_cplt; // 指定操作的返回代码 
            UCHAR ncb_reserve[10]; // 保留字段 
            HANDLE ncb_event; // Win32事件句柄
        } NCB, *PNCB;

此外,编程时应注意调用NetBIOS函数的同步和异步问题。NetBIOSming令调用本身均为同步,即在完成指定ming令之前,会一直调用NetBIOS模块。而在实际编程时,我们通常需要进行异步调用,即希望允许多个客户机同时与服务器进行连接,这就需要让NetBIOSming令与异步标志逻辑或(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编号组成的一个数组。

:2011-03-16 16:35:38

然后为每个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); 
        }

上一页  [1] [2] 


利用NetBIOS进行Windows网络编程