“网络基本输入/输出系统”(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的比较典型的应用是远程计算机的Mac地址、名称和所在工作组等信息。 PS:网上很多说NetBIOS网络编程已经过时了,不过还是稍做了解吧~(以下为个人概括的五点,如有什么不是很对的地方,欢迎指正)重点1:它是基于会话层的服务。重点2:它可以让两台机器相互连接,建立通信。重点3:它可以基于UDP或者TCP实现。重点4:实现这个NetBIOS与基本的TCP/IP编程的最大不同点就是向另一台机发送的是NetBIOS请求包,接收的是NetBIOS回应包,然后对NetBIOS回应包进行解释,其编程思路与TCP/IP编程基本相似,只是一些recv和send的东西不同。重点5: 它的实现,也可以在“cmd”中使用nbtstat指令体现。 下面是三个与NetBIOS网络编程稍有关系的实例应用:01. GetNetBIOSNames.cpp -- 获取LANA上所有NetBIOS名字02. GetMacAddress.cpp -- 获取网络适配器上的MAC地址03. NbtStat.cpp -- 获取网络中指定计算机的基本信息 04获取ip 05获取mac地址 06获取机器码
源代码:01. GetNetBIOSNames.cpp -- 获取LANA上所有NetBIOS名字
// GetNetBIOSNames.cpp -- 获取LANA上所有NetBIOS名字
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <Nb30.h>
#pragma comment(lib, "netapi32.lib")
// Set LANANUM and LOCALNAME as appropriate for your system
#define LANANUM 0
#define LOCALNAME "UNIQUENAME"
#define NBCheck(x) if (NRC_GOODRET != x.ncb_retcode) { \
printf("Line %d: Got 0x%x from NetBios()\n", \
__LINE__, x.ncb_retcode); \
}
void MakeNetbiosName (char *, LPCSTR);
BOOL NBReset (int, int, int);
BOOL NBAddName (int, LPCSTR);
BOOL NBListNames (int, LPCSTR);
BOOL NBAdapterStatus (int, PVOID, int, LPCSTR);
int main()
{
// 初始化,清空本地名字表和会话表
if (!NBReset (LANANUM, 20, 30)){
return -1;
}
// 向本地名字表中添加UNIQUENAME
if (!NBAddName (LANANUM, LOCALNAME)) {
return -1;
}
// 列出本地名字表中的名字
if (!NBListNames (LANANUM, LOCALNAME)) {
return -1;
}
printf ("Succeeded.\n");
system("pause");
return 0;
}
/**
* NCB结构体的定义:
* typedef struct _NCB{
* UCHAR ncb_command; // 指定命令编码以及表明NCB结构体是否被异步处理的标识
* 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; // 指定会话执行接收操作的超时时间
* void (CALLBACK * ncb_post)(struct NCB);
* UCHAR ncb_lana_num; // 指定LANA编号
* UCHAR ncb_cmd_cplt; // 指定命令完成标识
* UCHAR ncb_reserve[X]; // 保留字段
* HANDLE ncb_event; // 指向事件对象的句柄
* }NCB, *PNCB;
**/
// 清空本地名字和会话表
BOOL NBReset (int nLana, int nSessions, int nNames)
{
NCB ncb;
memset (&ncb, 0, sizeof (ncb)); // 清空ncb结构体
ncb.ncb_command = NCBRESET; // 执行NCBRESET命令,复位局域网网络适配器,清空指定LANA编号上定义的本地名字表和会话表。
ncb.ncb_lsn = 0; // 分配新的lana_num资源,Netbios()函数成功执行了NCBCALL命令后返回的编号
ncb.ncb_lana_num = nLana; // 设置lana_num资源,指定本地网络名字编号。
ncb.ncb_callname[0] = nSessions; // 设置最大会话数
ncb.ncb_callname[2] = nNames; // 设置最大名字数
Netbios (&ncb); // 执行NCBRESET命令
NBCheck (ncb); // 如果执行结果不正确,则输出ncb.ncb_retcode
// 如果成功返回TRUE,否则返回FALSE
return (NRC_GOODRET == ncb.ncb_retcode);
}
// 向本地名字表中添加名字
BOOL NBAddName (int nLana, LPCSTR szName)
{
NCB ncb;
memset (&ncb, 0, sizeof (ncb)); // 清空ncb结构体
ncb.ncb_command = NCBADDNAME; // 执行NCBDDNAME命令,向本地名字表中添加一个唯一的名字
ncb.ncb_lana_num = nLana; // 设置lana_num资源,指定本地网络名字编号。
MakeNetbiosName ((char*) ncb.ncb_name, szName); // 将szName赋值到ncb.ncb_name中
Netbios (&ncb); // 执行NCBRESET命令
NBCheck (ncb); // 如果执行结果不正确,则输出ncb.ncb_retcode
// 如果成功返回TRUE,否则返回FALSE
return (NRC_GOODRET == ncb.ncb_retcode);
}
// Build a name of length NCBNAMSZ, padding with spaces.
// 将szSrc中的名字赋值到achDest中,名字的长度为NCBNAMESZ
// 如果不足,则使用空格补齐
void MakeNetbiosName (char *achDest, LPCSTR szSrc)
{
int cchSrc = strlen ((char*)szSrc); // 取名字的长度
if (cchSrc > NCBNAMSZ){
cchSrc = NCBNAMSZ;
}
memset (achDest, ' ', NCBNAMSZ);
memcpy (achDest, szSrc, cchSrc);
}
// 列出指定LANA上所有的名字
BOOL NBListNames (int nLana, LPCSTR szName)
{
int cbBuffer; // 获取数据的缓冲区
ADAPTER_STATUS *pStatus; // 保存网络适配器的信息
NAME_BUFFER *pNames; // 保存本地名字信息
HANDLE hHeap; // 当前调用进程的堆句柄
hHeap = GetProcessHeap(); // 当前调用进程的堆句柄
cbBuffer = sizeof (ADAPTER_STATUS) + 255 * sizeof (NAME_BUFFER);// 分配可能的最大缓冲区空间
pStatus = (ADAPTER_STATUS *) HeapAlloc (hHeap, 0, cbBuffer);// 为pStatus分配空间
if (NULL == pStatus){
return FALSE;
}
// 获取本地网络适配器信息,结果保存到pStatus中
if (!NBAdapterStatus (nLana, (PVOID) pStatus, cbBuffer, szName)){
HeapFree (hHeap, 0, pStatus);
return FALSE;
}
// 列出跟在ADAPTER_STATUS结构体后面的名字信息
pNames = (NAME_BUFFER *) (pStatus + 1);
for (int i = 0; i < pStatus->name_count; i++){
printf ("\t%.*s\n", NCBNAMSZ, pNames[i].name);
}
HeapFree (hHeap, 0, pStatus);// 释放分配的堆空间
return TRUE;
}
// 获取指定LANA的网络适配器信息
// nLana, LANA编号
// pBuffer, 获取到的网络适配器缓冲区
// cbBuffer, 缓冲区长度
// szName, 主机名字
BOOL NBAdapterStatus (int nLana, PVOID pBuffer, int cbBuffer, LPCSTR szName)
{
NCB ncb;
memset (&ncb, 0, sizeof (ncb)); // 清空ncb结构体
ncb.ncb_command = NCBASTAT; // 设置执行NCBASTAT命令,获取本地或远程网络适配器的状态
ncb.ncb_lana_num = nLana; // 设置LANA编号
ncb.ncb_buffer = (PUCHAR) pBuffer; // 将获取到的数据保存到参数pBuffer中
ncb.ncb_length = cbBuffer; // 设置缓冲区长度
MakeNetbiosName ((char*) ncb.ncb_callname, szName);// 设置参数ncb.ncb_callname
Netbios (&ncb); // 执行NetBIOS命令
NBCheck (ncb); // 如果执行不成功,则输出返回值
// 如果成功返回TRUE,否则返回FALSE
return (NRC_GOODRET == ncb.ncb_retcode);
}
</pre><pre name="code" class="cpp">
</pre><pre name="code" class="cpp">#include <winsock2.h> //该头文件定义了Socket编程的功能
#include <stdio.h> //该头文件声明了输入输出流函数
#include <stdlib.h> //该头文件定义了一些通用函数
#include <httpext.h> //该头文件支持HTTP请求
#include <windef.h> //该头文件定义了Windows的所有数据基本型态
#include <Nb30.h> //该头文件声明了netbios的所有的函数
#pragma comment(lib,"ws2_32.lib") //连接ws2_32.lib库.只要程序中用到Winsock API 函数,都要用到 Ws2_32.lib
#pragma comment(lib,"netapi32.lib") //连接Netapi32.lib库,MAC获取中用到了NetApi32.DLL的功能
//在Win32平台上的Winsock编程都要经过下列的基本步骤:定义变量->获得Winsock版本->加载Winsock库->
//初始化->创建套接字->设置套接字选项->关闭套接字->卸载Winsock库,释放所有资源。
void CheckIP(void) //定义checkIP函数,用于取本机的ip地址
{
WSADATA wsaData;
char name[155]; //定义用于存放获得主机名的变量
char *ip; //定义IP地址变量
PHOSTENT hostinfo;
//调用MAKEWORD()获得Winsocl版本的正确值,用于下面的加载Winscok库
if ( WSAStartup( MAKEWORD(2,0), &wsaData ) == 0 )
{ //加载Winsock库,如果WSAStartup()函数返回值为0,说明加载成功,程序可以继续往下执行
if( gethostname ( name, sizeof(name)) == 0)
{ //如果成功,将本地主机名存放入由name参数指定的缓冲区中
if((hostinfo = gethostbyname(name)) != NULL)
{ //这是获取主机,如果获得主机名成功的话,将返回一个指针,指向hostinfo,hostinfo为PHOSTENT型的变量。
ip = inet_ntoa (*(struct in_addr *)*hostinfo->h_addr_list);
//inet_addr()函数把地址串转换为IP地址
//调用inet_ntoa()函数,将hostinfo结构变量中的h_addr_list转化为标准的IP地址(如202.197.11.12.)
printf(" IP地址: %s/n",ip); //输出IP地址
}
}
WSACleanup( ); //卸载Winsock库,并释放所有资源
}
}
//通过WindowsNT/Win2000中内置的NetApi32.DLL的功能来实现的。首先通过发送NCBENUM命令,获取网卡的
//数目和每张网卡的内部编号,然后对每个网卡标号发送NCBASTAT命令获取其MAC地址。
int getMAC(char * mac) //用NetAPI来获取网卡MAC地址
{
NCB ncb; //定义一个NCB(网络控制块)类型的结构体变量ncb
typedef struct _ASTAT_ //自定义一个结构体_ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff [30];
}ASTAT, *PASTAT;
ASTAT Adapter;
typedef struct _LANA_ENUM //自定义一个结构体_ASTAT_
{
UCHAR length;
UCHAR lana[MAX_LANA]; //存放网卡MAC地址
}LANA_ENUM;
LANA_ENUM lana_enum;
// 取得网卡信息列表
UCHAR uRetCode;
memset(&ncb, 0, sizeof(ncb)); //将已开辟内存空间ncb 的值均设为值 0
memset(&lana_enum, 0, sizeof(lana_enum)); //清空一个结构类型的变量lana_enum,赋值为0
//对结构体变量ncb赋值
ncb.ncb_command = NCBENUM; //统计系统中网卡的数量
ncb.ncb_buffer = (unsigned char *)&lana_enum; //ncb_buffer成员指向由LANA_ENUM结构填充的缓冲区
ncb.ncb_length = sizeof(LANA_ENUM);
//向网卡发送NCBENUM命令,以获取当前机器的网卡信息,如有多少个网卡,每个网卡的编号(MAC地址)
uRetCode = Netbios(&ncb); //调用netbois(ncb)获取网卡序列号
if(uRetCode != NRC_GOODRET)
return uRetCode;
//对每一个网卡,以其网卡编号为输入编号,获取其MAC地址
for(int lana=0; lana<lana_enum.length; lana++)
{
ncb.ncb_command = NCBRESET; //对网卡发送NCBRESET命令,进行初始化
ncb.ncb_lana_num = lana_enum.lana[lana];
uRetCode = Netbios(&ncb);
}
if(uRetCode != NRC_GOODRET)
return uRetCode;
// 准备取得接口卡的状态块取得MAC地址
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBASTAT; //对网卡发送NCBSTAT命令,获取网卡信息
ncb.ncb_lana_num = lana_enum.lana[0]; //指定网卡号,这里仅仅指定第一块网卡,通常为有线网卡
strcpy((char*)ncb.ncb_callname, "*"); //远程系统名赋值为*
ncb.ncb_buffer = (unsigned char *)&Adapter; //指定返回的信息存放的变量
ncb.ncb_length = sizeof(Adapter);
//接着发送NCBASTAT命令以获取网卡的信息
uRetCode = Netbios(&ncb);
// 取得网卡的信息,并且如果网卡正常工作的话,返回标准的冒号分隔格式。
if(uRetCode != NRC_GOODRET)
return uRetCode;
//把网卡MAC地址格式转化为常用的16进制形式,输出到字符串mac中
sprintf(mac,"%02X-%02X-%02X-%02X-%02X-%02X",
Adapter.adapt.adapter_address[0],
Adapter.adapt.adapter_address[1],
Adapter.adapt.adapter_address[2],
Adapter.adapt.adapter_address[3],
Adapter.adapt.adapter_address[4],
Adapter.adapt.adapter_address[5]
);
return 0;
}
int main(void) //主函数,程序的入口
{
CheckIP(); //调用CheckIP()函数获得,输出IP地址
char mac[200];
getMAC(mac); //调用getMAC()函数获得,输出MAC地址
printf(" mac地址 : %s /n",mac);
return 0;
}
// jiqimaDlg.cpp : implementation file
//
#include "stdafx.h"
#include "jiqima.h"
#include "jiqimaDlg.h"
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <httpext.h>
#include <windef.h>
#include <Nb30.h>
#pragma comment(lib, "Netapi32.lib")
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CJiqimaDlg dialog
CJiqimaDlg::CJiqimaDlg(CWnd* pParent /*=NULL*/)
: CDialog(CJiqimaDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CJiqimaDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CJiqimaDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CJiqimaDlg)
DDX_Control(pDX, IDC_EDIT_JIQIMA, m_Editjiqima);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CJiqimaDlg, CDialog)
//{{AFX_MSG_MAP(CJiqimaDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_GETJIQIMA, OnGetjiqima)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CJiqimaDlg message handlers
BOOL CJiqimaDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CJiqimaDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CJiqimaDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CJiqimaDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
void CJiqimaDlg::OnGetjiqima()
{
// TODO: Add your control notification handler code here
unsigned long s1,s2;
unsigned char vendor_id[]="------------";//CPU提供商ID
CString str1,str2,str3;
// 以下为获得CPU ID的汇编语言指令
// _asm // 得到CPU提供商信息
// {
// xor eax,eax // 将eax清0
// cpuid // 获取CPUID的指令
// mov dword ptr vendor_id,ebx
// mov dword ptr vendor_id[+4],edx
// mov dword ptr vendor_id[+8],ecx
// }
// str1.Format("%s",vendor_id);
_asm // 得到CPU ID的高32位
{
mov eax,01h
xor edx,edx
cpuid
mov s2,eax
}
str2.Format("%08X-",s2);
_asm // 得到CPU ID的低64位
{
mov eax,03h
xor ecx,ecx
xor edx,edx
cpuid
mov s1,edx
mov s2,ecx
}
str3.Format("%08X-%08X\n",s1,s2);
char mac[200];
NCB ncb;
typedef struct _ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff [30]; //保存网卡信息
}ASTAT, * PASTAT;
ASTAT Adapter;
typedef struct _LANA_ENUM
{
UCHAR length;
UCHAR lana[MAX_LANA]; //存放网卡地址
}LANA_ENUM ;
LANA_ENUM lana_enum;
UCHAR uRetCode;
memset(&ncb, 0, sizeof(ncb));
memset(&lana_enum, 0, sizeof(lana_enum));
ncb.ncb_command = NCBENUM;
ncb.ncb_buffer = (unsigned char *)&lana_enum;
ncb.ncb_length = sizeof(LANA_ENUM);
uRetCode = Netbios(&ncb);
for(int lana=0; lana<lana_enum.length; lana++)
{
ncb.ncb_command = NCBRESET;
ncb.ncb_lana_num = lana_enum.lana[lana];
uRetCode = Netbios(&ncb);
if(uRetCode == NRC_GOODRET)
break;
}
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBASTAT;
ncb.ncb_lana_num = lana_enum.lana[0];
strcpy((char*)ncb.ncb_callname, "*");
ncb.ncb_buffer = (unsigned char *)&Adapter;
ncb.ncb_length = sizeof(Adapter);
uRetCode = Netbios(&ncb);
sprintf(mac,"%02X%02X%02X%02X%02X%02X",
Adapter.adapt.adapter_address[0],
Adapter.adapt.adapter_address[1],
Adapter.adapt.adapter_address[2],
Adapter.adapt.adapter_address[3],
Adapter.adapt.adapter_address[4],
Adapter.adapt.adapter_address[5]
);
CString m_str1;
CString str;
m_str1.Format("%s",mac);
str2=str2+str3;
str = m_str1 + str2;
m_Editjiqima.SetWindowText(str);
}