用C++在Windows上开发SNMP扩展代理(SNMP extension agent) DLL

    9月接到一个开发任务,要做一个SNMP代理和第三方网管软件对接,将本公司系统的数据输出给第三方。之前开发过SNMP设备的监控服务,实现了数据查询采集上报及遥控的功能,已投入实际生产,所以对SNMP协议并不陌生。SNMP代理的功能正好与之相反,虽然没开发过,但也知道难度并不高,短时间内能做出来。
    开发工具仍旧是用了20年的C++,之前做SNMP数据采集用的是indy库里的IdSNMP,这次就无法用了,要换别的开发工具。如果从UDP底层网络数据包从零开始对SNMP协议进行解码、编码,工作量会比较大,预计需要2-3个月开发时间,不在客户的接受时间范围内,只能寻找SNMP的其它开发工具了。目前比较常见有NetSNMP、SNMP++、WinSNMP。前2者有开源代码,可以跨平台,支持SNMP v3,但可能需要比较长的时间去寻找、阅读文档,另外不知软件包里有无bug、能否长期稳定运行。微软的WinSNMP只能支持SNMP v2c,以DLL的形式开发,作为Windows SNMP服务的扩展代理(extension agent)去运行。虽然在开发时和以后使用中,都要在Windows上手工安装SNMP服务(Windows缺省不安装Windows服务),但这只需要安装一次,不算啥问题。主要困难是以前只开发过很简单的DLL,对DLL的内部技术细节不了解,测试调试会比较困难。但既然是扩展代理,那么很多工作就已经被微软做了,我们可以减少编码时间,快速完成开发任务。并且对微软SNMP软件的稳定性比较放心,就选WinSNMP了。
    选好开发工具后,就在网上找资料。找到了2个很有用的demo:http://www.codeproject.com/Articles/9024/How-to-develop-a-SNMP-extension-agent-DLL 和 http://support.microsoft.com/kb/192796/en-us 。仔细看完大家的评论,下载源代码稍微改了一下,就测试成功了,选WinSNMP的信心更坚定了。其次比较有用的就是MSDN了,要仔细阅读文档,了解各个SNMP函数的输入、输出、返回、注释,有些函数的介绍要重复看几次才会彻底了解其用法。还找到一份源代码,当对某些SNMP函数的原理不了解时,仔细看看会有相当帮助: https://stuff.mit.edu/afs/sipb/project/wine/src/wine-0.9.37/dlls/snmpapi/main.c 。其余网上的大部分资料就是对英文的翻译了。另外有一份必看的资料,就是CB自带的帮助文档,这个是不用说的。值得一提的是,有一个免费软件snmptools,把windows计数器翻译为SNMP的OID,也是用微软SNMP服务扩展代理的方式实现的。
    学习完大部分资料后,并不是马上就开始正式编码,而是写通信协议、安装使用说明书、配置文件。通信协议、安装使用说明,是要交给客户,并且要给公司留底。设计出一个好的配置文件,可以让软件更加通用,能在不同环境下重复使用,而无需重新编译。有了初稿,才真正开始编码,编码中间也会经常参考、慢慢完善这些文档。
    以上工作大概占用了一半的时间,下面介绍几个在编码中值得注意的地方:
    首先就是怎样在没有GUI的DLL里调试,之前自己用TThread写了一个单线程、高并发、轻量级的日志模块,用在DLL里发现退出线程时会卡死。原因很快就找到了,就是在DLL里面没有消息循环,TThread退出时调用Synchronize函数会卡死。解决方案也很简单,就是改为_beginthreadex来建立线程。
    导出SnmpExtensionInit这些函数,在前面加__declspec(dllexport) 关键字,编译连接时会有告警,改为在模块定义def文件里导出,就没警告了。
    AsnObjectIdentifier的成员ids是一个指针,要保证一直指向有效的内存空间。否则运行时容易出现异常。
    内存申请、释放要小心,例如:SnmpUtilOidCpy和SnmpUtilOidFree配对,SnmpUtilVarBindListCpy和SnmpUtilVarBindListFree配对,SnmpUtilMemAlloc和SnmpUtilMemFree配对,千万不可混用。和SNMP服务交互时,谁申请内存、谁释放内存,一定要看MSDN或CB帮助文档,正确使用,否则轻则内存泄露,重则只能祈祷上帝。例如收到GetNext的指令,读入vbl里面的OID后要将其释放掉,换成在DLL里已申请好内存的OID,交给SNMP服务去释放。
    处理GetNext指令时,达到最后OID时,不能返还错误,而要返回本代理软件后续最小的OID。例如本代理负责处理的OID前缀为1.3.6.1.4.1.22853,那么此时要返还1.3.6.1.4.1.22854。响应SNMP服务的指令时,如果中间处理过程中出错,就不能修改vbl,只能设置错误号和索引,返回一个错误号。这2点是codeproject网站上代码的bug。
    SnmpExtensionTrap函数里的参数pTimeStamp,微软是建议使用SnmpSvcGetUptime函数的返回值来初始化。

    尽量避免在DllMain函数里写代码,推荐除了拷贝HINSTANCE hinst这个句柄,其它初始化工作一律放在SnmpExtensionInit函数里执行,退出DLL的清理操作一律放在SnmpExtensionClose函数里执行,否则容易出现死锁。具体原因和情况请学习下列博文: http://blog.csdn.net/breaksoftware/article/category/1294271 。刚开始我也没注意到这个问题,在DllMain函数里写了一部分初始化和退出清理的代码,学习了这一系列文章马上改正。虽然写代码时没出现死锁,但以后实际运行出现死锁就麻烦了。毕竟SNMP服务会自动调用SnmpExtensionInit和SnmpExtensionClose函数。
    注册表编写完后要仔细检查,不要犯低级错误。其中[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ExtensionAgents]里面的Key可以用任意的字符串,不一定要按网上的文档用从1开始的整数,注意Key对应的注册表路径是用2个\\分开的。当然[HKEY_LOCAL_MACHINE\SOFTWARE\SNMP\CurrentVersion]里面Pathname对应的路径,也可以自己定义。但注意路径也是用2个\\分开的。

    使用JSON格式来写配置文件,写完后一定要用第三方工具来校验JSON语法是否完全正确。JSON的容错性比INI差,任何一个小错误都会导致JSON解析失败。由于对JSON的解码、编码的速度无要求,选用了使用非常方便的JSONCPP。刚开始是用0.5.0版本,但碰到了JSONCPP不能当全局变量的bug。马上改用最新版0.7.0,此bug已被修正。
    开发工作从9月19日开始,到10月6日基本完成,在我的Windows 7 64位版上长时间运行测试很稳定,非常满意,其中实际编码时间大概是10天左右,不算太长。但8号在Windows Server 2008 R2上测试时发现其SNMP服务不调用DLL,在Windows的系统日志里报错,event ID为1102,错误信息为:“SNMP 服务忽视扩展代理键 dll 我的DLL文件名称 因为它已丢失或配置不当”。这可不行,我们的软件一般是运行在Windows Server上的,而不是Windows 7。网上搜此信息,都是说注册表配置错误,显然不符合我的情况。因为把我开发的DLL换成网上下载的demo DLL是可以正常工作的。并且重建一个最简单的DLL,里面只有SnmpExtensionInit、SnmpExtensionQuery函数,函数里只有几行最基本的代码,也不能在Server上工作。但把这个最简单的DLL改为无框架的DLL,却正常了,但这时添加业务代码,编译连接报很多错误。经过1天的反复折腾,故障依旧,不得其解,难道要把项目拆成2个工程,一个简单的DLL和SNMP服务通信,一个服务抓取数据传给DLL?这样的话,又需要几天时间来开发、测试,进程之间通信比同一个进程复杂,运行起来还不一定简单稳定可靠。最后决定找个工具测试一下之前开发的DLL为什么不能启动。在网上发现一个法国网站(http://www.silogix.fr/Outils/slxsnmpinfo.aspx),下载SlxSNMPInfo这个工具后,发现在Windows Server上需要一个文件:cc32140mt.dll。其实自己写个EXE用LoadLibrary函数调用DLL,捕获异常也能发现这个问题。一查这个DLL的来源,就明白故障的原因是Link with Dynamic RTL缺省被选中了。之前建DLL时,Link with the Delphi Runtime Library是自动未选中的,已手工把Link with runtime packages改为未选中。但Link with Dynamic RTL未手工改为未选中,现在找到原因了,去掉这个选项前面的勾,重新编译,在多台电脑上测试问题毫无问题,最后高高兴兴把文档发给客户。

    整个项目从开始到基本完成,大概20天左右,其中很多时间用在学习、文档上。因为以前的学习和实践积累,编码时间比较短。怎样编码,让软件高效、健壮地运行,就无需细说了。总结这个项目的收获:加深了对SNMP协议的理解;对DLL开发有了新的认识经验。

    作者联系方式:QQ 139250065,欢迎咨询、讨论。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Windows操作系统提供了一种称为SNMP(Simple Network Management Protocol)的网络管理协议。该协议允许网络管理员通过查询和获取目标设备的信息来监控和管理网络设备。 在Windows中,要使用SNMP协议进行网络管理,我们可以调用DLL(Dynamic Link Library,动态链接库)文件来实现DLL文件包含了一系列函数和程序代码,可以被不同的应用程序调用和使用。 要调用DLL文件进行SNMP操作,我们需要使用一种编程语言,如C++或C#来编写代码。通过代码,我们可以加载和初始化SNMP库,并使用相应的函数来执行SNMP命令。 首先,我们需要通过调用LoadLibrary函数来加载SNMP DLL文件。这会将DLL文件加载到我们的应用程序的地址空间中,并返回一个句柄,供我们后续使用。 然后,我们可以使用GetProcAddress函数来获取DLL中的函数地址。SNMP DLL文件通常会提供一些用于发送SNMP消息的函数,如SnmpOpen、SnmpGet等。我们可以通过GetProcAddress函数将这些函数的地址保存到变量中。 接下来,我们可以通过调用这些函数来执行特定的SNMP操作。例如,我们可以使用SnmpOpen函数来打开一个SNMP会话,使用SnmpGet函数来查询目标设备的特定信息,然后使用SnmpClose函数关闭会话。 在完成SNMP操作后,我们可以使用FreeLibrary函数来卸载DLL文件,并释放之前加载的资源。 需要注意的是,使用SNMP调用DLL需要有一定的编程经验和对SNMP协议的了解。对于不熟悉编程的用户,可以使用一些现成的SNMP管理软件来实现类似的功能,而无需直接调用DLL文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值