学习要求: 了解NetBIOS网络编程方法,掌握获取网卡MAC地址的编程原理,学会在Win32 Console Application方式下使用Visual C++ 6.0与NetBIOS编写获取网卡MAC地址的程序;
众所周知,一个网卡对应唯一MAC地址,对一些应用程序来说,获取网卡MAC地址有时是必要的,使用VC提供的NetBIOS网络编程接口可以方便获取网卡MAC地址。
NetBIOS中所用的函数声明、常数等等均是在头文件nb30.h内定义的,nb30.h中一些类型在wtypes.h中定义,因此一般在网络程序开始的include语句中将wtypes.h放在nb30.h之前。另外,使用NetBIOS还须连接库netapi32.lib。
调用NetBIOS函数时,可使用NetBIOS中提供的一个唯一的函数调用:
uRetCode = netbios(&ncb);
入口参数: &ncb为指向一网络控制块(NCB)的指针。在调用NetBIOS函数前要先预置好该NCB结构中的一些有关字段;
出口返回: 返回码uRetCode正常时应为0,返回的具体结果会包含在NCB结构的一些字段中。
NetBIOS编程时要用到的一些结构有:
1. NCB结构:
在该NCB结构中,包含了为执行一个NetBIOS命令相应需预先准备(在调用前)或命令执行结果(在调用后)的全部信息,
typedef struct _NCB{
UCHAR ncb_command; //用于指定要执行的NetBIOS命令, 一些常见的NetBIOS命令如附表所示。
//命令可以有非异步(也称等待方式)和异步(也称非等待方式)两种方式执行:
//将命令名和常量ASYNCH进行OR操作后的结果放入ncb_command则置为异
//步(非等待)方式;若将命令名放入ncb_command则置为非异步(等待)方式。
//在采用等待方式执行时,NetBIOS函数调用要等到该命令操作完成之后才返回执
//行下一条语句。如果采用非等待(异步)方式执行,可以在调用NetBIOS前设置
//ncb_post(后处理程序地址)或设置ncb_event(触发事件对象句柄),让NetBIOS
//在完成命令后自动唤醒该后处理程序或触发相关事件,在采用异步方式时,
//NetBIOS函数调用当场立即返回,执行下一条语句,并且带回“立即返回代码”
//ncb_retcode,而当命令执行完时,又返回一个“最终返回代码”ncb_cmd_cplt,
//你的程序可以根据这两个代码确定该命令是否成功执行。
UCHAR ncb_retcode; //用于指定操作的立即返回代码,若返回值为NRC_GOODRET(即0),表示
//无立即差错
UCHAR ncb_lsn; //本地会话号。在使用面向连接的会话方式通信时,建立会话时,首先,两个站分别使
//用增加名字命令NCBADDNAME或增加组名命令NCBADDGRNAME增加本站名
//字,然后,一个站须发出NCBLISTEN命令,另一站发出NCBCALL命令,每一方
//成功执行完命令,都会在NCB中返回一个本地会话号ncb_lsn,此后,两个站使用
//NCBSEND、NCBRECV命令发送、接收数据时,都要在命令调用前在NCB中的
//ncb_lsn字段赋值引用该本地会话号
CHAR ncb_num; //本地名字号。在使用无连接的数据报方式通信时,每次须先使用增加名字命令
//NCBADDNAME或增加组名命令NCBADDGRNAME增加本站名,这时,NetBIOS
//函数都会返回这个新增加的名字一个名字号。此后,两个站使用NCBDGSEND、
//NCBDGRECV命令发送、接收数据报时,都要在命令调用前在NCB中的ncb_num
//字段赋值引用该本地名字号
PUCHAR ncb_buffer; //指向数据缓冲区的指针。发送时,在NetBIOS函数调用前,该缓冲区中须放置要发
//送的实际数据;接收时,为NetBIOS函数调用后返回的接收数据;对其他命令来说,
//如NCBENUM,缓冲区中是NetBIOS函数调用后返回的预定义的结构LANA_ENUM
//;对NCBSTAT命令来说,缓冲区里就是NetBIOS函数调用命令执行后逻辑网卡返
//回的统计结果
WORD ncb_length; //指定缓冲区的长度,以字节数为单位,发送时为发送数据的字节数;接收时
//NetBIOS会将该值设为实际收到的字节数,若指定的缓冲区不够大,NetBIOS会
//返回 NRC_BUFLEND错误
UCHAR ncb_callname[NCBNAMSZ]; //用于指定远方机器应用程序用的名字
UCHAR ncb_name[NCBNAMSZ]; //用于指定本方机器应用程序用的名字
UCHAR ncb_rto; //规定接收操作时的会话超时时间(以0.5秒为单位)
UCHAR ncb_sto; //规定发送操作时的会话超时时间(以0.5秒为单位)
void (CALLBACK *ncb_post) (struct _NCB *); //若使用异步命令(即非等待方式时),操作完成后调用
//的后处理程序地址
UCHAR ncb_lana_num;
//
指定LAN Adapter逻辑网卡号
UCHAR ncb_cmd_cplt; //完成操作后的最终返回代码,操作成功,正常返回NRC_GOODRET(即0);
//若用异步操作,操作正进行期间NetBIOS会将这个值设为NRC_PENDING
UCHAR ncb_reserve[10]; //保留,必须为0
HANDLE ncb_event; //事件对象的句柄。当一个异步命令被接受时,事件对象被置为不发信号状态,
//当该异步命令完成时,事件对象被置为发信号状态,当NetBIOS函数返回非0值
//时也会发信号给事件。ncb_command为非异步(等待)方式时或ncb_post非
//0时,该ncb_event应为0。
}NCB, *PNCB;
2.LANA_ENUM结构:
nb30.h中还定义了另外一个很重要的结构LANA_ENUM,当主机中有两块以上逻辑网卡(一物理网卡绑定某一协议为一逻辑网卡)时,使用NCB命令NCBENUM后(该命令只能在Windows NT/2000/XP中使用),该结构中会含有返回的逻辑网卡个数及每个逻辑网卡号,该结构的定义如下:
typedef struct LANA_ENUM {
UCHAR length; //系统中含有的逻辑网卡个数
UCHAR lana [MAX_LANA]; //数组中放各个逻辑网卡号
} LANA_ENUM, *PLANA_ENUM;
其中: lana [MAX_LANA]定义了一个存放所有逻辑网卡信息的数组,length表示该数组的长度,即为本机所带逻辑网卡的个数。
3. 网卡结构ASTAT和网卡状态结构ADAPTER_STATUS
(1)网卡结构ASTAT,其中使用了网卡状态结构ADAPTER_STATUS:
typedef struct
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff[30];
} ASTAT;
(2)网卡状态结构ADAPTER_STATUS:
typedef struct _ADAPTER_STATUS {
UCHAR adapter_address[6]; //网卡MAC地址
UCHAR rev_major;
UCHAR reserved0;
UCHAR adapter_type;
UCHAR rev_minor;
WORD duration;
WORD frmr_recv;
WORD frmr_xmit;
WORD iframe_recv_err;
WORD xmit_aborts;
DWORD xmit_success;
DWORD recv_success;
WORD iframe_xmit_err;
WORD recv_buff_unavail;
WORD t1_timeouts;
WORD ti_timeouts;
DWORD reserved1;
WORD free_ncbs;
WORD max_cfg_ncbs;
WORD max_ncbs;
WORD xmit_buf_unavail;
WORD max_dgram_size;
WORD pending_sess;
WORD max_cfg_sess;
WORD max_sess;
WORD max_sess_pkt_size;
WORD name_count;
} ADAPTER_STATUS, *PADAPTER_STATUS;
使用NetBIOS获取网卡MAC地址的编程提示:
1. 在调用netbios NCBASTAT命令前,我们可将指向ADAPTER_STATUS结构的指针值赋给缓冲区指针ncb.ncb_buffer,
ASTAT Adapter;
ncb.ncb_buffer = (unsigned char *) &Adapter; //两个指针指向同一个地方
ncb.ncb_length = sizeof(Adapter); // 设置ncb_buffer缓冲区长度
这样在执行NCBASTAT命令后,由于ncb.ncb_buffer指针所指的缓冲区中为命令执行后逻辑网卡返回的统计结果(其中包含MAC网卡地址、一组有关网络负荷和差错的统计数字、网卡所在工作站的名字表以及每个名字对应的状态信息等),我们使用与其同一地址的ADAPTER_STATUS结构的adapter_address[6]字段便可取出其中的网卡地址。
2. 由于ncb_command字段用于指定要执行的netbios命令,在执行一个netbios函数调用前应先赋值ncb_command字段,另外,还要设置NCB中一些与该命令相关的字段:
(1)首先,我们要获取当前计算机有多少个逻辑网卡,可用以下一段代码来实现:
NCB ncb; //网络控制块
LANA_ENUM lana_enum; //该结构包含系统中的逻辑网卡数目及每个逻辑网卡号
memset(&ncb, 0, sizeof(ncb) ); //NCB结构清零
ncb.ncb_buffer=(unsigned char *)&lana_enum;
ncb.ncb_length=sizeof(lana_enum);
ncb.ncb_command = NCBENUM; //枚举系统逻辑网卡命令
UCHAR uRetCode = Netbios(&ncb); //uRetCode返回0为正确返回;非0为出错返回
int num = lana_enum.length; //读出返回的逻辑网卡个数
(2)然后,如果对第一块逻辑网卡进行初始化(复位)可用以下代码实现:
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBRESET; //初始化逻辑网卡命令
ncb.ncb_lana_num = lana_enum.lana[0]; //第一块逻辑网卡号
uRetCode = Netbios(&ncb );
(3)此后,如查询第一块逻辑网卡包括MAC地址等在内的信息可用以下代码实现:
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBASTAT; //对逻辑网卡统计信息命令
ncb.ncb_lana_num = lana_enum.lana[0]; //第一块逻辑网卡号
strcpy((char*)ncb.ncb_callname,"* "+0x0);
// ncb_callname 中要设置netbios调用时对方使用的名字,*表示不限定,这里
//用来表示要对本机统计信息。netbios中使用的名字由16个字节组成,前15个
//字节为ASCII码,若不满,要用空格填满。这里最后一字节为NULL(0x0)
ASTAT Adapter;
ncb.ncb_buffer = (unsigned char *) &Adapter;
ncb.ncb_length = sizeof(Adapter);
uRetCode = Netbios(&ncb);
…… //对返回结果中的网卡MAC地址等部分进行提取处理
3.通常进行任何NetBIOS调用之前,不要一开始就填写结构内的各个成员,而应将这个NCB结构清零。 清零可用
memset(&NCBobject, 0, sizeof(NCBobject) )
语句实现。
4. 使用NetBIOS调用Netbios(&ncb)还可以在站点间进行数据的发送与接收操作。NetBIOS调用前,要预行设置ncb结构中各成员参数:ncb_buffer中放数据块内容,ncb_length放数据块长度,ncb_callname中放远方欲通信的机器应用名字,ncb_name中为本方机器应用名,ncb_lana_num中放本方发送或接收使用的逻辑网卡号,发送或接收的过程,根据采用面向连接的会话通信方式还是无连接的数据报通信方式的不同,其过程步骤也不一样,见下图所示,其它祥细参数设置与编程细节可参考相关的MSDN帮助文档。
附表
NCB结构成员ncb_command中设置的常用命令
Code | Meaning |
NCBADDNAME | Adds a name to the local name table. |
NCBASTAT | Retrieves the status of either a local or remote adapter. When this code is specified, the ncb_buffer member points to a buffer to be filled with an ADAPTER_STATUS structure, followed by an array of NAME_BUFFER structures. |
NCBCALL | Opens a session with another name. |
NCBCANCEL | Cancels a previous pending command. |
NCBCHAINSEND | Sends the contents of two data buffers to the specified session partner. |
NCBDELNAME | Deletes a name from the local name table. |
NCBDGRECV | Receives a datagram from any name. |
NCBDGRECVBC | Receives a broadcast datagram from any name. |
NCBDGSEND | Sends datagram to a specified name. |
NCBENUM | Windows NT/2000 or later: Enumerates LAN adapter (LANA) numbers. When this code is specified, the ncb_buffer member points to a buffer to be filled with a LANA_ENUM structure. |
NCBFINDNAME | Determines the location of a name on the network. When this code is specified, the ncb_buffer member points to a buffer to be filled with a FIND_NAME_HEADER structure followed by one or more FIND_NAME_BUFFER structures. |
NCBHANGUP | Closes a specified session. |
NCBLISTEN | Enables a session to be opened with another name (local or remote). |
NCBRECV | Receives data from the specified session partner. |
NCBRESET | Resets a LAN adapter. An adapter must be reset before it can accept any other NCB command that specifies the same number in the ncb_lana_num member. Use the following values to specify how resources are to be freed: ﹡If ncb_lsn is not 0x00, all resources associated with ncb_lana_num are to be freed. ﹡If ncb_lsn is 0x00, all resources associated with ncb_lana_num are to be freed, and new resources are to be allocated. The ncb_callname[0] byte specifies the maximum number of sessions, and the ncb_callname[2] byte specifies the maximum number of names. A nonzero value for the ncb_callname[3] byte requests that the application use NAME_NUMBER_1. |
NCBSEND | Sends data to the specified session partner. |
NCBSENDNA | Sends data to specified session partner and does not wait for acknowledgment. |
NCBUNLINK | Unlinks the adapter. |
联系客服