65.9K
CodeProject 正在变化。 阅读更多。
Home

WinSNMP 库的 C++ 包装器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (22投票s)

2005年9月16日

8分钟阅读

viewsIcon

522524

downloadIcon

10719

一个用于在 Windows 上开发 SNMP 管理器(客户端)的 C++ 库。

SNMP walk console screenshot

引言

本文档演示了如何为 Windows API(在此例中为 WinSNMP 库)构建 C++ 包装器,以隐藏用户界面的复杂性。本文档中称为库的包装器旨在简化 WinSNMP 管理器(客户端)的开发,并利用 STL。请注意,本文档的目的不是提供有关 SNMP 或 WinSNMP API 本身的帮助,也不是提供一个功能齐全的库。为此,可以参考其他资源,例如 CodeProject 的其他文章或开源项目。

背景与动机

本文档的内容是我在对 STL 和 WinSNMP 库进行实验时,整理之前存储的一些代码的结果。撰写本文档的初衷只是为了与社区分享我的工作。

包含在示例文件中

文件包括完整的源代码(文本格式)和 UML 模型(Visio 格式),根据 GPL 许可分发,以及 VS 2005 Beta 2 项目文件。还包括已编译并运行的示例工具:SNMPGET、SNMPSET、SNMPWALK、SNMPTRAP 和 SNMPSCAN。

理解模型

全局概览

如果您了解 UML,可以通过查看图 1 的类图来在脑海中构建整体图景。

UML Class Diagram

图 1 - UML 类图。

在此图中,所有库类都被表示出来,包括主要的类关系和公共方法。

SNMPManager 类

此类封装了 WinSNMP 的启动和关闭过程。要使用该库,您需要在执行任何其他操作之前调用管理器上的 startup 方法。可以通过 getManager 方法获取该管理器的唯一实例的指针(顺便说一下,这里可以看到单例模式的实际应用)。WinSNMP 和库的清理代码会在类析构函数中自动调用,该析构函数会在应用程序终止前被调用。getDisplayInformation 方法将返回一个字符串,其中包含有关已安装的 WinSNMP 库的各种信息项,例如版本、供应商和选项。

SNMPTracer 类

SNMPTracer 是一个单例类,用于控制协议消息的跟踪,可用于测试/调试目的(要查看其工作原理,请使用 -T 选项运行任何示例)。如果启用了跟踪(通过 enable 方法),在库运行期间,所有交换的 SNMP 协议数据单元的字符串表示形式将发送到控制台,包括时间戳。您可以浏览代码,了解如何通过 Win32 API 处理控制台。

SNMPObject 类

这是库中最有趣也是最有用的类。该类的实例代表 SNMP 数据项(或变量),包括寻址(OID)和/或值(用于读取或写入)。

OID

SNMPObject 的对象标识符可以通过 getOIDsetOID 方法以字符串格式访问。所有解析和转换为二进制格式的操作都由内部处理。还提供了用于将 OID 与对象 OID 进行比较的帮助方法(compareOID)。这些方法映射到 WinSNMP 库的词典式 OID 比较函数。

语法

语法方法(getSyntaxsetSyntaxgetSyntaxAsString)将以二进制或字符串形式返回或设置对象的内部类型。可用类型为:SNMP_INTSNMP_OCTETSSNMP_OPAQUESNMP_BITSSNMP_CNTR64SNMP_CNTR32SNMP_GAUGE32SNMP_TIMETICKSSNMP_UINT32SNMP_OIDSNMP_IPADDR。请注意,这些常量不一定与 WinSNMP 库中相应的常量相同。这样做的目的是构建一个包装器库,它不强制用户依赖于 WinSNMP 库本身。如果您打算在将来用另一个实现替换 WinSNMP 库,并希望避免重写用户代码,这种模式可能非常有用。

对象的值可以使用 getValueXXXsetValueXXX 方法设置或读取。基于字符串的方法尤其有用,因为它们提供了解析和生成任何类型值的代码。请检查内部代码,了解这是如何实现的。

显示信息

与其他库类一样,getDisplayInformation 返回一个包含人类可读信息的字符串,在本例中是 OID、值和语法。

SNMPSession 和 SNMPRemoteAgent 类

SNMPSession 类的对象保存有关一个或多个 SNMP 远程代理的信息。此时,此类并不十分重要,除非您了解 WinSNMP 库。您甚至不需要使用它,正如您在示例或 SNMPRemoteAgent 类的实现中所见。SNMPRemoteAgent 类的对象保存有关 SNMP 远程代理的信息。这包括名称(管理器机器的主机名或 IP 地址)、SNMP 社区名称和服务器远程端口。这些属性在类构造函数中设置。对于您需要与之交换信息的每个远程代理,都必须实例化一个 SNMPRemoteAgent 类的对象。

SNMP 请求

所有 SNMP 请求都使用 SNMPRequest 抽象类的实例执行。对于每种 SNMP 交换类型(Get、Set 或 Get Next),都存在一个具体子类,应根据您希望访问的 SNMP 服务来使用。要发出 SNMP 请求,您必须创建一个 SNMPRequest 对象并调用其 execute 方法。然后,您可以等待该对象,直到事务终止(通过 wait 方法),或者定期检查操作是否已完成(通过 isTerminated 方法)。实现本质上是异步的,并且不执行重试。可以通过 canceltimedOut/succeeded/getErrorXXX 方法取消请求以及获取上次执行请求状态的结果信息。

Get 请求

可以通过实例化 SNMPRemoteAgentGetRequest 类来创建 Get 请求。构造函数接收要从中检索 OID 的 OID 和要从中请求对象的远程代理。要了解更多信息,请查看 SNMPGET 示例。

Set 请求

可以通过实例化 SNMPRemoteAgentSetRequest 类来创建 Set 请求。在这种情况下,输入的 SNMPObject 包括 OID 和要在服务请求中设置的值。要了解更多信息,请查看 SNMPSET 示例。

Get Next 请求

与 Get 请求类似,Get Next 请求(SNMPRemoteAgentGetNextRequest)返回具有相邻 OID 的对象。要了解更多信息,请查看 SNMPWALK 示例。

SNMPTrap 类

SNMPTrap 类的处理方式与请求类非常相似,但允许您接收 SNMP 陷阱的指示。要使用它,请创建一个类实例,传入您希望监听的 OID 以及提供该 OID 的代理。然后调用 enable 方法开始接收指示。您也可以通过 disable 方法禁用指示。处理指示需要更多的工作,因为您需要派生 SNMPSession 类并覆盖 processTrap 方法。下面显示了一个使用示例,取自 SNMPTRAP 示例。

class MySession : public SNMPSession {
public: 
    virtual void processTrap(const SNMPObject & obj) {
        std::cout << obj.getDisplayInformation() << "\n";
    }
};

MySession session;
SNMPRemoteAgent ragent(host, community, 0, &session);
SNMPTrap trap(oid, &ragent);
trap.enable();

请注意,陷阱处理尚未经过测试,因此可能存在问题。请随意检查它是否有效!

其他类

异常

您可以调用的绝大多数库方法(几乎所有方法)都可能抛出 SNMPExceptionSNMPErrorException 类的异常(实际上只抛出 SNMPErrorException)。每个异常都会为您提供有关错误的详细信息,包括关于原因的消息和错误 ID。要打印此信息,请使用 toString(如果您需要完整的异常堆栈,请使用 toStringStack)。为了帮助编写库代码,使用宏(请参阅 SNMPException.h)设置了一个小型异常框架。

Sets

集合类(SNMPRequestSetSNMPRemoteAgent)非常直接,支持存储和集合操作。

Using the Code

对于那些认为代码和注释是最准确文档的人来说,下面将展示 SNMPGET 示例的完整代码,并附有其他注释。

int main(int argc, char* argv[]) {
    try {   
        // startup
        SNMPManager::getManager()->startup(); 
        
        // holds the host name or address of the agent
        std::string host = "172.18.200.90";
        // holds the community name
        std::string community = "public";
        // holds the response time out value for requests
        int tmo = 1000;
        // holds the agent port 
        unsigned int port = 0; // zero means the SNMP default port
        // holds the number of OIDs to request
        int oidcount = 0;
        // holds the OIDs to request
        std::string oidarray[512];
        
        // (...) here the command line arguments are parsed
        // to fill in the previous variables
        
        // create the remote agent
        SNMPRemoteAgent ragent(host, community, port);    
        // create the SNMP objects containing the OIDs to request
        SNMPObject * oids = new SNMPObject[oidcount];
        for(int i=0; i<oidcount; i++) {            
            oids[i].setOID(oidarray[i]);
        }
        // create the get request
        SNMPRemoteAgentGetRequest getReq(oids, oidcount);
        // perform the request
        getReq.execute(&ragent);
        // wait for the response
        getReq.wait(tmo);
        // destroy request OIDs
        delete [] oids;    
        // process result    
        if(!getReq.succeeded()) {
            if(!getReq.timedOut()) {
                std::cout << getReq.getErrorAsString();
            } else {
                std::cout << "timeout";
            }
        } else {
            for(int i=0; i<getReq.getResultCount(); i++) {    
                std::cout << getReq.getResult(i)->getDisplayInformation() << "\n";
            }
        }
        std::cout << "\n";
        return 0;
    } catch (SNMPException * pe) {
        // Process any errors
        std::cout << "\n\n";
        std::cout << pe->toStringStack();
        std::cout << "\n";
        delete pe;
    }
    return 0;
}

探索代码和示例

所有代码都包含在一组 *.h*.cpp 文件中。考虑到文件名,找到您想要的类相当直接。示例包含在 main.cpp 文件中。要编译每个示例,您需要在文件开头解释的相应宏。

所有示例都是命令行工具,接受一组参数来执行各种操作。每个工具都包含一些通用选项,例如:打印帮助(-h 或 -?)、启用跟踪(-T)、设置超时(t:<n>)或设置代理的远程端口(p:<n>)。

  • SNMPGET: 根据 OID 读取一组对象值。
  • SNMPSET: 写入一个 SNMP 对象,提供其 OID、值,以及可选的语法。
  • SNMPWALK: 显示代理中从给定 OID 开始的所有对象。
  • SNMPSCAN: 扫描您的网络以检测 SNMP 代理的可用性。
  • SNMPTRAP: 如果有效,则处理来自代理的陷阱。

最后

我希望本文档对您有所帮助,如果您认为有帮助,请发表评论。当然,在设计方面仍有很大的改进空间。虽然我没有计划进行任何更新,但欢迎您提出任何评论以及设计或编码方面的替代方案。

© . All rights reserved.