Android實現snmp協議(一)

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版本吧,功夫不負有心人。
這個網站的閉源包提供了這個功能,做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();
	}
做到這份上,跟華爲的老師聯繫上了,他們跟我說年前比較忙,拖到年後才能詳談需求業務,我也暫時擱置此項目,去做別的事了。也許有新的情況,我會寫個第二篇,希望對大家有幫助。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章