分享
 
 
 

基于WinSNMP的网络管理程序设计----原理与实践(第三章)

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

第三章 Snmp在Windows下的实现----WinSNMP编程原理

在Windows下实现SNMP协议的编程,可以采用Winsock接口,在161,162端口通过udp传送信息。在Windows 2000中,Microsoft已经封装了SNMP协议的实现,提供了一套可供在Windows下开发基于SNMP的网络管理程序的接口,这就是WinSNMP API。

3.1 什么是WinSNMP

WinSNMP的目的是为在Windows下开发基于SNMP的网络管程序提供解决方案。它为SNMP网管开发者提供了必须遵循的开放式单一接口规范,它定义了过程调用、数据类型、数据结构和相关的语法。

图3.1显示了一个网络管理站(NMS)和网络管理代理(Agent)之间端到端的SNMP连接中WinSNMP所处的层次。这是一个WinSNMP的参考模型。

图3.1WinSNMP参考模型

总的来说,WinSNMP以函数的形式封装了SNMP协议的各部分(在VC++6.0开发环境中体现为wsnmp32.dll、wsnmp32.lib和winsnmp.h),且针对SNMP是使用UDP的特点而设置了消息重传、超时机制等。

3.2 一些基本概念

在WinSNMP编程中,我们需要考虑的基本概念主要有以下几点:

 SNMP支持层次

 Entity/Context转换模式

 本地数据库

 会话

 异步模式

 内存管理

下面我们将分别对它们作介绍。

3.2.1 SNMP支持层次(Levels of SNMP Support)

WinSNMP支持四个层次的SNMP操作:

 Level 0 = 只有消息编码/解码

 Level 1 = Level 0 + 与SNMPv1代理的通信

 Level 2 = Level 1 + 与SNMPv2代理的通信

 Level 3 = Level 2 + 与其它SNMPv2管理站的通信

因为SNMP协议支持SNMPv1与SNMPv2的共存,所以WinSNMP实现能提供对两个版本协议的支持。

SnmpStartup函数能返回当前WinSNMP实现所能提供的最大支持层次。

3.2.2 Entity/Context转换模式(Entity/Context Translation Modes)

WinSNMP应用程序能够让WinSNMP实现把entity和context参数按不同的方式解释:

(1)按字面解释为SNMPv1代理的地址和共同体(community)字符串。

(2)解释为SNMPv2的party和context标识符(context IDs)。

(3)通过查询本地数据库将其转换为各自的SNMPv1或SNMPv2元素。

三种Entity/Context转换模式如下:

SNMPAPI_TRANSLATED = 通过本地数据库查询转换

SNMPAPI_UNTRANSLATED_V1 = 转换为地址和共同体(community)字符串

SNMPAPI_UNTRANSLATED_V2 = SNMPv2的party和context IDs.

我们可以通过SnmpStartup函数获得当前默认的entity/context转换模式,SnmpSetTranslatedMode函数可以用来设置entity/context转换模式。

当在系统中采用SNMPv1协议时,我们可以将其设置为SNMPAPI_UNTRANSLATED_V1,具体实现如下:

HSNMP_ENTITY hAgent;

HSNMP_CONTEXT hView;

LPCSTR entityName = “202.120.86.71”;

smiOCTETS contextName;

contextName.ptr = “public”;

contextName.len = lstrlen (contextName.ptr);

hAgent = SnmpStrToEntity (hSomeSessin, entityName);

hView = SnmpStrToContext (hSomeSession, const &contextName);

通过这样的设置,我们就可以在161端口通过UDP访问IP地址“202.120.86.71”上的SNMP代理了。

3.2.3 本地数据库(Local Database)

本地数据库主要存储重传模式(RetransmitMode)、重试次数(Retry)、超时(timeout)、转换模式(TranslateMode)等值。我们可以对其中的数据进行读(get)、写(set)操作。

3.2.4 会话(session)

会话是用来管理WinSNMP应用程序和WinSNMP实现之间的连接,由SnmpCreateSession(推荐)或SnmpOpen函数创建。会话是资源管理的最小单位,也是WinSNMP应用程序和WinSNMP实现之间通信管理的最小单位。一个良好的WinSNMP应用程序应该使用会话结构逻辑地管理它的操作,并将实现中的资源需求控制在最小。

调用SnmpCreateSession或SnmpOpen函数创建一个会话时,会返回一个“session id”,这是一个句柄(handle)变量,WinSNMP用它来管理自己的资源。应用程序最终应调用SnmpClose函数将会话释放。

3.2.5 异步模式(Asynchronous Model)

当代编程模式的一个很大特点就是消息驱动。WinSNMP采用了异步消息驱动模式,主要基于两个原因:

(1) 异步消息驱动模式非常适合于面向对象理论、SNMP分布式管理模型以及Windows编程、运行环境。

(2) SNMP再管理站和代理之间传送数据没有什么特别的传输机制,它基本上是基于数据报的,没有在远程实体之间建立实际通道(虚电路)。这样的事实使得WinSNMP非常适合采用异步模式。

现代的消息驱动程序必须响应各种重要事件,有些则完全依赖于异步关系。事实上,WinSNMP API中几乎所有函数都有异步成分,有些则是完全异步的。有三个非常重要的异步函数:

 SnmpSendMsg (发送数据)

 SnmpRecvMsg (接收数据)

 SnmpRegister (注册接受trap消息)

WinSNMP的整个编程模式就是基于异步的,我们将在后面做详细介绍。

3.2.6 内存管理(Memory Management)

在Windows编程中,内存管理一向是一个令人头疼的问题。在这里,我们将对WinSNMP的内存管理做一个较为详尽的描述。

WinSNMP包括三种不同的内存“对象”:

 句柄式资源 (HANDLE’d Resources)

 C风格(以NULL结尾)的字符串

 WinSNMP API结构类型

3.2.6.1 句柄式资源 (HANDLE’d Resources)

有五种句柄式资源的变量:

 Sessions

 Entities

 Contexts

 Protocol Data Units (PDUs)

 VarBindLists (VBLs)

所有句柄对象都表示为“HSNMP_<object_tag>”的形式,它为WinSNMP实现(以DLL方式)所拥有。

3.2.6.2 C风格字符串 (C-Stytle Strings)

C风格的字符串主要用来为通用的字符串表示与Entity和对象标识符(OID)对象之间的转换提供便利。WinSNMP中使用C风格字符串的函数有:SnmpStrToEntity、SnmpEntityToStr、SnmpStrToOid、SnmpOidToStr。

C风格字符串的内存分配、管理和释放完全由应用程序负责。因此我们还需要传递“size”参数给使用它的函数。

3.2.6.3 描述符 (Descriptors)

WinSNMP中有三种结构类型:

 smiOCTETS

 smiOID

 smiVALUE

前两种类型的定义如下:

typedef struct {

smiUINT32 len; /*unsigned long integer 类型,表示ptr中的字节数*/

smiLPBYTE ptr; /*指向包含octet string的字节数组的far指针*/

} smiOCTETS;

typedef struct {

smiUINT32 len; /**unsigned long integer 类型,表示ptr中无符号长整形的个数*/

smiLPUINT32 ptr; /*指向由OID各个标识符组成的无符号长整形数祖的far指针*/

} smiOID;

smiVALUE稍微复杂一点,它的定义如下:

typedef struct { /* smiVALUE portion of VarBind */

smiUINT32 syntax; /* Insert SNMP_SYNTAX_<type> */

union {

smiINT sNumber; /* SNMP_SYNTAX_INT

SNMP_SYNTAX_INT32 */

smiUINT32 uNumber; /* SNMP_SYNTAX_UINT32

SNMP_SYNTAX_CNTR32 SNMP_SYNTAX_GAUGE32 SNMP_SYNTAX_TIMETICKS */

smiCNTR64 hNumber; /* SNMP_SYNTAX_CNTR64 */

smiOCTETS string; /* SNMP_SYNTAX_OCTETS

SNMP_SYNTAX_BITS

SNMP_SYNTAX_OPAQUE

SNMP_SYNTAX_IPADDR

SNMP_SYNTAX_NSAPADDR */

smiOID oid; /* SNMP_SYNTAX_OID */

smiBYTE empty; /* SNMP_SYNTAX_NULL

SNMP_SYNTAX_NOSUCHOBJECT

SNMP_SYNTAX_NOSUCHINSTANCE

SNMP_SYNTAX_ENDOFMIBVIEW */

} value; /* union */

} smiVALUE;

当一个应用程序得到一个smiVALUE变量时,首先必须检查它的“syntax”成员,已决定怎样取到它的第二个成员。

当“syntax”成员变量显示“value”值是一个smiOCTETS或smiOID对象时,我们就应该考虑内存管理,约定如下:

(1) 当其作为输入参数时,应用程序负责为变长对象分配内存;

(2) 当其作为输出参数时,由WinSNMP实现(表现为DLL)为变长对象分配

内存。

3.2.6.4 内存的释放

WinSNMP应用程序必须负责释放所有通过调用WinSNMP API函数所分配的资源,主要有以下三类函数:

 SnmpFree<xxx>: 释放Entity、Context、Pdu、Vbl、Descriptor

 SnmpClose : 关闭会话

 SnmpCleanup : 必须在程序结束之前调用,释放所有资源

应用程序推荐使用上述的顺序来释放所有的WinSNMP资源。

3.3 WinSNMP基本编程模式

WinSNMP API按照SNMP协议封装了各种操作,包括PDU、VarBindList以及协议操作的各项函数。我们可以按照SNMP协议的描述,调用WinSNMP相关函数,完成一次完整的SNMP。我们下面将以笔者完整的系统(采用SNMPv1协议)为例,具体描述WinSNMP的一般编程模式。我们分发送请求消息与接受响应消息两部分来实现。

3.3.1 WinSNMP发送请求消息

WinSNMP发送请求消息的过程可以分为四个部分,主要有:WinSNMP的初始化、PDUs的创建、发送信息以及资源的释放。

3.3.1.1 WinSNMP的初始化

(1) 调用SnmpStartup函数启动WinSNMP。

(2) 调用SnmpCreateSession函数创建一个会话session。

(3) 调用SnmpSetRetransmitMode函数设置重传模式。

(4) 调用SnmpSetRetry函数设置重传次数。

(5) 调用SnmpSetTimeout函数设置超时时间。

其中第3、4、5步都是对本地数据库的操作,完成了对WinSNMP相关参数的设置。

3.3.1.2 创建协议数据单元(PDUs)

在创建PDU之前,我们必须先创建变量绑定表(varbindlists)。

(1) 调用SnmpStrToOid函数创建读取对象的OID,例如,我们创建MIB变量ipInReceives(一个实例的OID为1.3.6.1.2.1.4.3.0),我们可以采用下面的代码:

LPCSTR name="1.3.6.1.2.1.4.3.0";

smiOID Oid;

SnmpStrToOid(name,&Oid);

(2) 调用SnmpCreateVbl函数创建变量绑定表。

HSNMP_VBL m_hvbl=SnmpCreateVbl(session,&Oid,NULL);/*NULL表示该OID的值为空*/

(3) 调用SnmpSetVb函数往变量绑定表中添加变量绑定,我们需先创

建一个OID,命名为Oid。

SnmpSetVb(m_hvbl,0,&Oid,NULL);/*0表示往变量绑定表中添加变量绑定,非0值表示修改此位置的变量绑定*/

创建好了变量绑定表后,我们调用SnmpCreatePdu函数创建协议数据单元,在这个函数中,我们必须设定error_index、error_status、request_id参数,它们都与协议中相应的量对应。

HSNMP_PDU m_hpdu=SnmpCreatePdu(session,SNMP_PDU_GET,

NULL,NULL,NULL,m_hvbl);

3.3.1.3 发送信息

我们首先调用SnmpStrToContext和SnmpStrToEntity函数创建共同体(community)字符串和代理entity,具体实现见3.2.2。

然后,我们调用SnmpSendMsg函数发送信息。

SnmpSendMsg(session,NULL,hAgent,hView,m_hpdu);

3.3.1.4 资源的释放

最后,我们应该释放所有分配的资源。

3.3.2 WinSNMP接受响应消息

还记得前面的SnmpCreateSession函数吗?它可以说是WinSNMP异步消息驱动模式的一个关键,让我们先来看看它的函数原型:

HSNMP_SESSION SnmpCreateSession(

HWND hWnd, // handle to the notification window

UINT wMsg, // window notification message number

SNMPAPI_CALLBACK fCallback, // notification callback function

LPVOID lpClientData // pointer to callback function data

);

它提供了两种方式的异步消息驱动,我们可以让WinSNMP在有响应消息到达时发送一个消息给系统,也可以让它自动调用一个函数。笔者采用了第一种方式,实现如下:

session=SnmpCreateSession(m_hWnd,wMsg,NULL,NULL);

我们可以给消息wMsg创建一个消息处理函数,在这个函数里处理消息的接收、信息的提取与处理等事务。

下面我们将具体描述WinSNMP接受响应消息的步骤。

(1) 调用SnmpRecvMsg函数接收数据

(2) 调用SnmpGetPduData函数从PDU中析取出数据,

(3) 调用SnmpCountVbl获得变量绑定列表中变量绑定的个数

(4) 调用SnmpGetVb函数取得PDU变量绑定表中每个变量绑定的OID及其对应的值,可以指明该变量绑定在变量绑定表中的位置。参考实现如下:

int nCount=SnmpCountVbl(varbindlist);

for(int index=1;i<=nCount;i++)

SnmpGetVb(varbindlist,index,&Oid,value[i]);

其中,index指定了变量绑定的位置,value[i]表示接收到的OID变量的值,是smiLPVALUE类型的,Oid表示接收到的变量绑定的OID。

对于value[i],我们可以参考3.2.6.3节,按照它的syntax成员,用select case语句,分别转换为字符串或整数类型。

(5) 调用SnmpOidToStr函数将Oid转换为字符串。并将接收到的Oid与发送数据包的各OID做比较,已决定各自值的归属。引用一段代码

if(strcmp(m_sOid[i],m_initOid[1])==0)

m_sDesr= str[i];

else if (strcmp(m_sOid[i],m_initOid[2])==0)

m_sSysOid=str[i];

else if (strcmp(m_sOid[i],m_initOid[3])==0)

m_sSysTime=str[i];

else if (strcmp(m_sOid[i],m_initOid[4])==0)

m_sName=str[i];

else if (strcmp(m_sOid[i],m_initOid[5])==0)

{m_sIpin=str[i];

m_nIpin=nIpin;}

else if(strcmp(m_sOid[i],m_initOid[6])==0)

m_sIpout=str[i];

当我们比较发送的OID与接收到的OID时,我们就知道了这个str[i]是属于哪个OID的值,应当放在哪里显示,以m_s开头的变量都代表了不同的label,这样,相应的值就在相应的字符串中显示。

通过这样的步骤,我们就完成了一个简单的SNMP网络管理程序的设计。但是,在具体的应用中,我们应该考虑更多的问题,如内存管理、错误处理等问题,还有很多问题需要我们在系统开发的过程中去发现、解决。下面,我将描述几个我在系统开发中遇到的问题,有的已经解决,有的还在探索中,希望能为同仁提供参考。

3.4 几个问题

3.4.1 读IP地址

前面讲到,IpAddress是SMIv1的一个应用数据类型,表示IP地址,它的定义为:

IpAddress::=[APPLICATION 0] IMPLICIT OCTET STRING(SIZE(4))

当我们读取一个表示IP地址的OID时,我们应该分别读出IpAddress四个字节的值,再将它们处理成我们平时见到的IP地址的形式。代码如下:

case SNMP_SYNTAX_IPADDR:

strIp.Format("%d",*m_value[i]->value.string.ptr);

strIp+=".";

strTemp.Format("%d",*(m_value[i]->value.string.ptr+1));

strIp+=strTemp;

strIp+=".";

strTemp.Format("%d",*(m_value[i]->value.string.ptr+2));

strIp+=strTemp;

strIp+=".";

strTemp.Format("%d",*(m_value[i]->value.string.ptr+3));

strIp+=strTemp;

3.4.2 GETNEXT操作的实现

GETNEXT是SNMP中用来读取表格变量的一个操作。在WinSNMP中,我们可以通过SnmpCreatePdu(session,SNMP_PDU_GETNEXT,NULL,NULL,NULL,m_hvbl)来创建一个GETNEXT操作的PDU。

关键的问题是我们如何对这个表格作遍历。(1).如何判断表格的结束;(2).在接收到响应消息时如何处理。

我们下面将以笔者系统为例,说明这些问题。我们将获得本机的路由表的一部分。先构造一个函数,代码如下:

void CSnmpManagerDlg::Next(LPTSTR Oid)

{

CString str(Oid);

if(!strcmp(str.Left(20),"1.3.6.1.2.1.4.21.1.7"))

{

file://处理接收到的数据

pSnmp.CreateVbl(Oid,NULL);

pSnmp.CreatePdu(SNMP_PDU_GETNEXT,NULL,NULL,NULL);

pSnmp.Send("127.0.0.1","public");

}

else

{

m_bNext=FALSE;

file://送去显示

}

}

我们把接收到的OID的前20位与路由next hop MIB变量("1.3.6.1.2.1.4.21.1.7")作比较,假如不等,就说明这一列已经结束。把数据送去显示或进一步处理。

我们可以为这一操作创建一个新的会话(session),或继续使用前面GET操作的会话。创建一个新的会话时,我们为这个会话指定一个消息处理函数,并在这个函数中,处理接收到的数据,以及调用Next(LPTSTR Oid)函数继续发送GETNEXT操作。

假如继续使用以前的会话,我们要依靠标志m_bNext,判断m_bNext的真假以决定是否继续发GETNEXT数据包。

void CSnmpManagerDlg::OnRecv()

{ file://接收、处理消息

if(m_bNext==TRUE)

Next(m_sOid);

}

这样,我们就完成了对表格中一列的遍历。同样,我们可以完成对整个表格的遍历,我们只需strcmp(str.Left(18),"1.3.6.1.2.1.4.21.1"),就可以获得整个表格的结束。再在Next(LPTSTR Oid)函数中用switch-case语句按各个MIB变量的值分类,就可以得到整个表格。

3.4.3 对表格变量的SET操作

在整个系统的开发中,我们曾经对SysName变量进行SET操作。证明是可行的。但当我们SET一个表格变量时,报告变量绑定(VB)错误,类型为bad value。可能有两个原因。

(1). 代理进程(Agent)不支持对这些表格变量的SET操作。(具体见RFC1212)

(2). 当SET一个表格变量时,我们应该对表格中的所有变量都赋值,并封装成一个PDU发出去。因为当我们用route add添加路由表时,必须指定所有的参数。并且,表格变量只允许添加与删除两种操作。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有