2015年一月初。接到华为一位老师的电话,让我帮忙做一款他们在北京展会上要用到的App,该App能够展示华为的网络设备运行状态并可以设置一些简单的参数,包括AP、LSW、AP、AR等。
华为老师跟我说他们是使用snmp协议v2c的版本进行管理的,这意味着我也得在Android设备实现该协议,并与他们的网络设备交互。
回去认真研究了一下并请教了一些对这块比较熟悉的小伙伴,总算对snmp有了一些粗浅的认识。
snmp是指简单网络设备管理协议,顾名思义就是对网络设备进行管理的通用标准协议,属于TCP/IP的应用层,snmp的服务器端占用的端口是161,客户端占用的是162(基于UDP协议)。
在windows上开启snmp协议可参照http://blog.csdn.net/zougangx/article/details/6977936。
需要注意的一点是:SNMP Service属性的安全选项卡中设置接受来自任何主机的snmp数据包,以便我们调试。
对已经建立了连接的两个设备之间,该协议使用了OID(对象标示符)作为查询的内容,OID的内容具体可参考http://www.cnblogs.com/aspx-net/p/3554044.html。OID有一部分是协议定义好的,有一部分设备厂商可以自己定义。
完成以上的步骤,并熟悉了基本的OID指令后,网上有写朋友说就可以使用Paessler SNMP Tester进行调试了,但是本人在实际操作中没有这么顺利,Paessler SNMP Tester一直显示noresponse,转而使用snmputil。(Paessler SNMP Tester和snmputil都是windows端测试snmp协议的工具,Paessler
SNMP Tester具有图形化界面,snmputil没有,关于snmputil的操作可以参考http://blog.chinaunix.net/uid-21857285-id-3340217.html)
在使用snmputil的时候出现error on SnmpMgrRequest
40错误,参考以下网址得到解决:http://blog.csdn.net/wqjsir/article/details/8472006,在这篇文章中对陷阱选项卡进行了配置。至此,我的snmputil和Paessler SNMP Tester才正常的运行起来!
在计算机的服务列表中,可以看到:
其中Trap消息是需要手动去开启的,而service是自动开启。至于snmp trap服务怎么使用,snmp service的陷阱选项卡的设置原因我也不是很清楚,也希望有人知道的话不吝赐教,暂时不影响我做项目也没深入研究下去。
当两个服务都开启后,可以使用netstat -an|findstr "162"或netstat -an|findstr "161"查看端口是否开发,161开启之后就已经可以做本地测试了。
snmp协议是TCP/IP协议,是用c系语言完成的,本人以前移植过的uip1.0也是用c语言写的。而Android必须使用Java来实现,为此,本人首先使用了snmp4j这个jar包,建立了Java工程,仿造官方文档的示例,coding如下(需引入snmp4j的两个jar包):
class SnmpManager { private TransportMapping transportMapping = null; private Snmp snmp = null; private int version; public final static int version1 = SnmpConstants. version1; public final static int version2c = SnmpConstants.version2c; public final static int version3 = SnmpConstants. version3; /** * 构造方法 * @param version */ public SnmpManager( int version) { this. version = version; try { // 设置成Udp协议 transportMapping = new DefaultUdpTransportMapping(); snmp = new Snmp( transportMapping); if (version == version3) { // 设置安全信息 USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0); SecurityModels. getInstance().addSecurityModel(usm); } transportMapping.listen(); } catch (Exception e) { e.printStackTrace(); System. out.println(e); } } /** * @param sync * @param bro * @param pdu * @param addr * 发送消息方法 */ public void sendMsg(boolean sync, final boolean bro, PDU pdu, String addr) { Address targetAddres = GenericAddress. parse(addr); Target target = null; if ( this. version == version3) { snmp.getUSM().addUser( new OctetString( "MD5DES"), new UsmUser( new OctetString( "MD5DES"), AuthMD5. ID, new OctetString("MD5DESUserAuthPassword" ), PrivDES.ID, new OctetString("MD5DESUserPrivPassword" ))); target = new UserTarget(); // 设置安全级别 target.setSecurityLevel(SecurityLevel. AUTH_PRIV); target.setSecurityName( new OctetString("MD5DES")); target.setVersion(SnmpConstants. version3); } else { target = new CommunityTarget(); if ( this. version == version1) { target.setVersion( version1); ((CommunityTarget) target).setCommunity(new OctetString("public" )); } else { target.setVersion( version2c); ((CommunityTarget) target).setCommunity(new OctetString("public" )); } } target.setAddress(targetAddres); target.setRetries(2); target.setTimeout(1000); if (sync) { // 发送报文 并且接受响应 ResponseEvent response = null; try { response = snmp.send(pdu, target); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); System. out.println(e); } // 处理响应 System. out.println( "Synchronize message from " + response.getPeerAddress() + "/nrequest:" + response.getRequest() + "/nresponse:" + response.getResponse()); } else { ResponseListener listener = new ResponseListener() { @Override public void onResponse(ResponseEvent event) { if (!bro) { ((Snmp) event.getSource()).cancel(event.getRequest(), this ); } // 处理响应 PDU request = event.getRequest(); PDU response = event.getResponse(); System. out.println( "Asynchronise message from " + event.getPeerAddress() + "/nrequest:" + request + "/nresponse:" + response); } }; try { snmp.send(pdu, target, null, listener); } catch (IOException e) { e.printStackTrace(); System. out.println(e); } } } } public class SnmpTest { public static String myVersion = ""; static boolean sync = false; static boolean bro = false; /** * 主函数 * @param args */ public static void main(String[] args) { SnmpManager manager = new SnmpManager(SnmpConstants.version2c ); // 构造报文 PDU pdu = new PDU(); // PDU pdu = new ScopedPDU(); version3使用 // 设置要获取的对象ID OID oids = new OID( "1.3.6.1.2.1.1.1.0"); // OID oids = new OID(new int [] { 1, 3, 6, 1, 2, 1, 1, 1, 0 }); pdu.add( new VariableBinding(oids)); // 设置报文类型 pdu.setType(PDU. GET); // ((ScopedPDU) pdu).setContextName(new OctetString("priv")); // 发送消息 其中最后一个是想要发送的目标地址 manager.sendMsg( true, true, pdu, "udp:127.0.0.1/161"); } }
运行结果如下:
等我把这段Java代码移植到Android工程中时,却不起作用了。我百度、google、stackoverflow等一些网站都看过了,在stackoverflow上有位朋友也是遇到和我一样的问题,有人回复snmp4j无法在Android上无法使用,究竟为什么,本人也不能解释给大家听,需要更厉害的人了!
因为是华为老师的项目,我不能这样尥蹶子,就继续查找相关资料,我想应该有人做出了类似snmp4j的Android版本吧,功夫不负有心人。
https://www.webnms.com/snmpapi-android/index.html
这个网站的闭源包提供了这个功能,做snmp的开发者,不妨研读一下,我就是在使用了此网站的开发包,完成了snmp的协议。