一.Windows下要想使用snmp协议读取设备数据,必须先开启snmp服务,开启过程参考以下网站:
windows开启snmp协议(windows server、windows11、windows 低版本)-CSDN博客
二.net-snmp库的编译(vs2015+)
1.获取net-snmp源码 从 net-snmp 官网 选择不同的版本下载,在此我使用的是5.8版本,也可以根据自己的需要下载不同的版本。
2.编译net-snmp静态库netsnmp.lib
下载完成后正常解压,解压完毕后进入到解压后的文件夹中,找到win32文件夹,点击进入。
然后再找到其中的libsnmp文件夹进入,进入后点击这个libsnmp.dsp文件进行vs2015升级,升级完成后会生成对应的.vcxproj文件
双击libsnmp.vcxproj文件进入到项目工程中
由于我的项目需要64位的netsnmp库,所以我还需要增加x64的编译选项(32位库可跳过此步骤),选择如下"配置管理器步骤"。
活动解决方案选择"release",平台选择"新建"
新平台选择"x64",复制设置选择"x86"平台,最后点击确定按钮即可。
选择刚添加的x64平台,项目属性如下设置(可以不用设置),最后点击项目的生成按钮即可
等待生成结果,生成的静态库文件位于下图信息所示位置。
三.程序集成
集成net-snmp库采用静态库集成的方式,我们需要已经生成的netsnmp.lib文件和源码包中include文件夹下的net-snmp文件
最终的snmp集成文件配置如下,include文件夹是net-snmp文件夹(位置上述已明确)和lib文件夹下的netsnmp.lib,这样程序文件的配置就按此结构进行配置。
我的程序名为SNMPDemo(大家可以自己写个demo配置),我之前配置的是绝对路径(相对路径报错)大家可以根据自己的情况进行配置。
四.源码解析
1.踩坑1 包含目录必须包含#include <winsock2.h> #include <ws2tcpip.h> 这两个依赖项,因为snmp是基于socket和UDP协议的,这是
2.踩坑2 初始化snmp结构体调用snmp_open函数前必须调用window SDK的宏SOCK_STARTUP打开socket传输,不然你的snmp_open函数返回永远是空的。
其余的代码注释已经很详细了,源码和动态库连接在以下连接(嫌麻烦的可以直接下载),本文旨在记录,有问题的可以评论或者私信反馈。
#pragma execution_character_set("utf-8")
#include "SNMPDemo.h"
#include <iostream>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/output_api.h>
#include <net-snmp/net-snmp-includes.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <QDebug>
#pragma comment(lib, "ws2_32.lib")
void SNMPDemo::SnmpGet()
{
//SNMP对象初始化
init_snmp("snmp get");
struct snmp_session sessionToPeer;
snmp_sess_init(&sessionToPeer);
sessionToPeer.peername = strdup("192.168.1.100:161");
sessionToPeer.version = SNMP_VERSION_2c;
sessionToPeer.community = (u_char*)("public");
sessionToPeer.community_len = strlen((const char*)sessionToPeer.community);
sessionToPeer.retries = 3;
sessionToPeer.timeout = 2000000;//等待时间2s(单位微妙)
SOCK_STARTUP;
struct snmp_session* sessionReturnedByLibrary = snmp_open(&sessionToPeer);
if (sessionReturnedByLibrary == NULL)
{
SOCK_CLEANUP;
int clib_errno, snmp_errno;
char* err_string;
snmp_sess_error(&sessionToPeer, &clib_errno, &snmp_errno, &err_string);
ui.textEdit->append(QString("初始化SNMP对象失败!失败原因:%1 失败代码:%2_%3").arg(err_string).arg(clib_errno).arg(snmp_errno));
return;
}
else
{
ui.textEdit->append(QString("初始化SNMP对象成功!地址:%1 社区名称:%2").arg(QString::fromUtf8(sessionToPeer.peername)).arg(QString::fromUtf8((char*)sessionToPeer.community)));
}
//创建SNMP GET请求PDU(Protocol Data Unit)
struct snmp_pdu* requestGetPdu = snmp_pdu_create(SNMP_MSG_GET);
if (!requestGetPdu)
{
ui.textEdit->append("初始化SNMP GET请求PDU对象失败!");
return;
}
//初始化OID变量
oid requestOid[MAX_OID_LEN]; //OID数组(Net-SNMP使用oid类型,即unsigned int数组)
size_t requestOidLength = MAX_OID_LEN; //OID数组的初始长度
//解析OID字符串 例子:设备描述Oid:.1.3.6.1.2.1.1.1.0
if (!snmp_parse_oid(".1.3.6.1.2.1.1.1.0", requestOid, &requestOidLength))
{
ui.textEdit->append("初始化OID对象失败!");
snmp_free_pdu(requestGetPdu);
return;
}
//向PDU中添加要查询的OID(NULL值表示请求获取该OID的值)每个OID对应一个变量绑定(Variable Binding)
snmp_add_null_var(requestGetPdu, requestOid, requestOidLength);
//发送请求并同步等待响应(同步请求时 创建的GetPdu(requestGetPdu)会自动释放
struct snmp_pdu* responseGetPdu = NULL; //存储响应PDU的指针
int snmpStatus = snmp_synch_response
(
sessionReturnedByLibrary, //已打开的SNMP会话句柄
requestGetPdu, //请求PDU
&responseGetPdu //用于接收响应PDU的指针
);
//检查SNMP请求状态
if (snmpStatus == STAT_SUCCESS)//底层通信成功(如收到响应)
{
//检查PDU是否包含错误(如权限不足、OID不存在等)
if (responseGetPdu->errstat == SNMP_ERR_NOERROR)
{
//遍历响应中的所有变量绑定(通常一个请求对应一个响应变量)
struct variable_list *snmpVariables;
for (snmpVariables = responseGetPdu->variables;
snmpVariables != NULL;
snmpVariables = snmpVariables->next_variable)
{
//处理不同类型的SNMP值
if (snmpVariables->type == ASN_OCTET_STR)
{
char* response = (char *)malloc(1 + snmpVariables->val_len);
if (response)//检查内存分配是否成功
{
memcpy(response, snmpVariables->val.string, snmpVariables->val_len);
response[snmpVariables->val_len] = '\0';
ui.textEdit->append(QString("接收到 OCTET_STR 值:%1").arg(response));
free(response);
response = NULL;
}
}
else if (snmpVariables->type == ASN_INTEGER)
{
long intValue = *snmpVariables->val.integer;
ui.textEdit->append(QString("接收到 INTEGER 值: %1").arg(intValue));
}
else if (snmpVariables->type >= ASN_CONTEXT)
{
//获取上下文标签号(低7位)
int contextTag = snmpVariables->type & 0x7F;
ui.textEdit->append(QString("接收到 ASN_CONTEXT 值: %1").arg(contextTag));
}
else
{
ui.textEdit->append(QString("不支持的 SNMP 类型:%1").arg(snmpVariables->type));
}
}
}
else
{
//PDU包含错误(如社区名错误、OID不可访问)
ui.textEdit->append(QString("SNMP返回错误:%1").arg(snmp_errstring(responseGetPdu->errstat)));
}
}
else
{
switch (snmpStatus)
{
case STAT_TIMEOUT:
ui.textEdit->append(QString("SNMP返回超时!"));
break;
case STAT_ERROR:
ui.textEdit->append(QString("SNMP连接失败!"));
break;
default:
ui.textEdit->append(QString("未知错误!"));
}
}
if (responseGetPdu)
{
snmp_free_pdu(responseGetPdu);
}
SOCK_CLEANUP;
}