在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添加路由表时,必须指定所有的参数。并且,表格变量只允许添加与删除两种操作。

Snmp在Windows下的实现----WinSNMP编程原理的更多相关文章

  1. windows下的socket网络编程

    windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了, ...

  2. windows下的socket网络编程(入门级)

    windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先 ...

  3. Qt4.8在Windows下的三种编程环境搭建

    Qt4.8在Windows下的三种编程环境搭建 Qt的版本是按照不同的图形系统来划分的,目前分为四个版本:Win32版,适用于Windows平台:X11版,适合于使用了X系统的各种Linux和Unix ...

  4. Windows下DNS ID欺骗的原理与实现

    域名系统(DNS)是一种用于TCP/IP应用程序的分布式数据库,它提供主机名字和IP地址之间的转换信息.通常,网络用户通过UDP协议和DNS服务器进行通信,而服务器在特定的53端口监听,并返回用户所需 ...

  5. Qt在Windows下的三种编程环境搭建

    尊重作者,支持原创,如需转载,请附上原地址:http://blog.csdn.net/libaineu2004/article/details/17363165 从QT官网可以得知其支持的平台.编译器 ...

  6. windows下ruby使用tk编程的方法

    我们知道tcl/tk是一个小巧的脚本语言,tk对于跨平台的CUI编程提供了很好的移植性,我们来一下windows下ruby中要想使用tk开发需要做哪些工作: 1 gem query -r tk #选择 ...

  7. windows下《Go Web编程》之Go环境配置和安装

    <Go Web编程>笔者是基于unix下讲述的,作为入门练手,我选择在windows下开发,全程按照目录进行... 一.安装 windows下需要安装MinGW,通过MinGW安装gcc支 ...

  8. Qt在Windows下的三种编程环境搭建(图文并茂,非常清楚)good

    尊重作者,支持原创,如需转载,请附上原地址:http://blog.csdn.net/libaineu2004/article/details/17363165 从QT官网可以得知其支持的平台.编译器 ...

  9. 【Qt开发】Qt在Windows下的三种编程环境搭建

    从QT官网可以得知其支持的平台.编译器和调试器的信息如图所示: http://qt-project.org/doc/qtcreator-3.0/creator-debugger-engines.htm ...

随机推荐

  1. CString和string头文件

    在使用了MFC库的工程中CString可以直接使用,在没有使用MFC库的工程中加入#include <atlstr.h> 要使用STL里的string,要加入#include <st ...

  2. react 自定义 百度地图(BMap)组件

    1.html 页面引入 相关js public/index.html <!DOCTYPE html> <html lang="en"> <head&g ...

  3. less 项目实战

    1.注释 less 的注释有两种方法,"/**/" 和 "//",前一种会在 css 文件中显示,后一种不会在 css 显示. 2.定义变量的方法:" ...

  4. 机器学习(Machine Learning)&amp;深度学习(Deep Learning)资料

    机器学习(Machine Learning)&深度学习(Deep Learning)资料 機器學習.深度學習方面不錯的資料,轉載. 原作:https://github.com/ty4z2008 ...

  5. Go语言中的单引号、双引号、反引号

    =Start= 搜索关键字: golang single quotes golang double quotes golang back quotes 参考结果: 结论写在最前:在Go语言中不倾向于使 ...

  6. python(32)- 模块练习Ⅱ:使用正则表达式实现计算器的功能

    开发一个简单的python计算器 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568 ...

  7. iOS移动开发周报-第17期

    lhq iOS移动开发周报-第17期 前言 欢迎国内的iOS同行或技术作者向我提交周报线索,线索可以是新闻.教程.开发工具或开源项目,将相关文章的简介和链接在微博上发布并 @唐巧_boy 即可. [摘 ...

  8. C#实现网段扫描

    目录1.使用的类2.获取本地主机IP地址3.远程查询4.实现网段的扫描 ---------------------------------------------------------------- ...

  9. php 静态成员(static)抽象类(abstract)和接口(interface)

    首先看一下静态成员(static)和普通成员(public; protect; private)的区别: 静态成员是属于类的,普通成员是属于对象的: 例如: <?php header(" ...

  10. 基于EasyIPCamera实现的数字网络摄像机IPCamera的模拟器IPC RTSP Simulator

    还记得去年在北京安博会上,看到一些厂家的展示台上,各种船舶.公路.车辆的高清视频直播,好奇这些数据是怎么接到现场的,现场成百上千家展台,不可能有那么大的带宽供应,细想数据肯定不是实时的,果然,盯着看了 ...