標籤:
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的協議。
Android實現snmp協議(一)