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的协议。

private void buttonFun() { noNull(); new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub api = new SnmpAPI(); session = new SnmpSession(api); SnmpPDU pdu = new SnmpPDU(); UDPProtocolOptions protocol = new UDPProtocolOptions(); protocol.setRemoteHost(getText(target)); protocol.setRemotePort(Integer.parseInt(getText(port))); pdu.setProtocolOptions(protocol); // Build Get request PDU pdu.setCommand(SnmpAPI.GET_REQ_MSG); pdu.setCommunity(getText(community)); if(version.getSelectedItemPosition()==0){ pdu.setVersion(SnmpAPI.SNMP_VERSION_1); }else if(version.getSelectedItemPosition()==1){ pdu.setVersion(SnmpAPI.SNMP_VERSION_2C); }else if(version.getSelectedItemPosition()==2){ //还需设置MD5 SHA认证密码等 pdu.setVersion(SnmpAPI.SNMP_VERSION_3); } pdu.setTimeout(Integer.parseInt(getText(timeout))); pdu.setRetries(Integer.parseInt(getText(retries))); // SnmpOID oid = new SnmpOID("1.3.6.1.2.1.1.1.0"); // SnmpOID oid = new SnmpOID((SnmpOID)(getText(oid))); // pdu.addNull(oid); String oidstr = getText(oid); String str[] = oidstr.split("\\."); int a[] = new int[9]; for(int i=0; i<str.length; i++){ a[i] = Integer.parseInt(str[i]); } // int a[] = { 1, 3, 6, 1, 2, 1, 1, 1, 0 }; SnmpOID oid = new SnmpOID(a); pdu.addNull(oid); SnmpPDU resp = null; try { session.open(); resp = session.syncSend(pdu); } catch (SnmpException e) { // TODO Auto-generated catch block e.printStackTrace(); System.out.println(e); } if (resp != null) { // return varBinds string UDPProtocolOptions options = (UDPProtocolOptions) resp.getProtocolOptions(); String resultString = "Response PDU received from " + options.getRemoteAddress() + ".\n"; // No I18N resultString = resultString + "Community = " + resp.getCommunity() + ".\n\n"; // No I18N if (resp.getErrstat() == 0) { resultString = resultString + "VARBINDS :\n\n"; // No I18N resultString = resultString + resp.printVarBinds(); } else { // Check for error in response resultString = resultString + resp.getError(); } Message msg = new Message(); msg.what = 1000; msg.obj = resultString; myHandler.sendMessage(msg); if(session != null) { session.close(); } if(api != null) { api.close(); } } else { String errorString = "Request timed out to: " + getText(target); // No I18N Message msg = new Message(); msg.what = 1000; msg.obj = errorString; myHandler.sendMessage(msg); if(session != null) { session.close(); } if(api != null) { api.close(); } } } }).start(); }
做到这份上,跟华为的老师联系上了,他们跟我说年前比较忙,拖到年后才能详谈需求业务,我也暂时搁置此项目,去做别的事了。也许有新的情况,我会写个第二篇,希望对大家有帮助。