最近做网络摄像机,有局点要求需要按SNMP协议上报消息,于是从网站http://www.net-snmp.org/下载了一个snmp源码包5.2.6,tar解压,交叉编译:
./configure --build=i686-linux --host=arm-merlin-linux CC=arm-merlin-linux-uclibc-gcc --with-mib-modules="examples/notification" --with-endianness=little LDFLAGS="-static"
(表示在x86的linuxOS上编译板上运行的程序,build是编译机,host是运行机,CC是编译器名,target不需要,因为编译出来的不是工具,加入一个notification的mib库,如何加入mib,看官网上有,最后使用小端静态编译)
如果在PC上用,则./configure --with-mib-modules="examples/notification" 简单的不得了
下面的代码演示如何从一个代理进程向管理进程上报trap消息(此处用来发心跳),当然也可以通过配置snmp让它自动上发trap消息,但我们想编程自己灵活控制.
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <time.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
int main()
{
netsnmp_session ,session;
static int i=0;
if (i==0)
{
snmp_sess_init(&session);
i=1;
}
session.version = SNMP_VERSION_2c;
session.peername = "192.168.1.3";
session.remote_port = 162;
session.community = (unsigned char*)"public";
session.community_len = strlen((char*)session.community);
session.retries = 3;
session.timeout = 2000;
session.sessid = 0;
snmp_session *ss = snmp_open(&session);
if (ss==NULL) return -1;
netsnmp_pdu *pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
long sysuptime = get_uptime();
char csysuptime[32];
sprintf(csysuptime,"%ld", sysuptime);
oid oid_sysuptime[] = {1,3,6,1,2,1,1,3,0};
int status = snmp_add_var(pdu, oid_sysuptime,OID_LENGTH(oid_sysuptime),'t', csysuptime);
if (status !=0) return -1;//一般不会出错
status = snmp_send(ss,pdu);
if (status == 0) return -2; //send要有东西发出去,返回0就不对了
snmp_close(ss);
return 0;
}
//以上的变量定义是按Effective C++的第XX条的原则:变量要到了用的时候才定义,个人喜欢这一条,所以坚持用了,除非是一些老的编译器,变量定义必须放在前面,否则我不会先定义一大堆的变量放在那晾着.
编译运行,管理进程未收到trap消息(何为管理进程,何为代理进程,看看资料或<tcp/ip详解 卷一就知道了)
抓包一看,发现消息发到161端口去了,上百度搜了一下,发现人家也有同样的问题,无果,最后几番折腾,才在一个SNMP_API.H文件里看到一个结构体stuct snmp_session一行注释:
/**UDP port number of peer (LO LONGER USED - USE peername INSTEAD)*/
u_short remote_port;
原来这个参数不用了,搞不懂为何要用peer_name来替代端口号,却还让remote_port这个字段放在这继续让程序员犯错,不用了为何不注释掉这个字段?让人家编译就能查错总比运行时诡异要好的多吧.
修改一下代码:
session.peername = "192.168.1.3:162";//应该是用这个格式
再试一下../sendtrap(make后生成的执行文件) 果然,管理进程正确收到消息了.抓包也可以看到destination port是162了.
再把
session.remote_port = 0;,不影响正确性.