1、采用 Java 进行 SNMP 通信的指南第 1 页/共 14 页Java 进行 SNMP 通信的指南采用 Java 进行 SNMP 通信的指南第 2 页/共 14 页1.概述在网络通信中,我们会经常遇到支持 SNMP 协议的网络设备打交道,支持 SNMP 协议的网络设备有很多,如各种带操作系统的服务器、路由器、交换机等,装有 UNIX 操作系统中的服务器一般都会支持 SNMP 协议,Windows 操作系统的服务器也可以通过”控制面板- 添加/删除程序-添加/删除 Windows 组件-管理和监视工具 ”中安装 SNMP 组件,如下图所示。图 1-1本文的目的不在于讲解 SNMP 的原理,关
2、于 SNMP 的原理及其信息组织结构 MIB 的详细资料,网上的资料很多,本文的目的就是针对在 Java 环境下解决如何实现 SNMP 客户端的调用操作以及如何实现 SNMP 服务器的简单模拟,把笔者的一些经验分享给大家。2.实现过程我们知道,SNMP 协议是基于 UDP 协议的,RFC-1157 就是 SNMP 协议详细规范,SNMP 协议经历了 3 个版本,分别是 SNMPV1、SNMPV2 和 SNMPV3。SNMPV1 是一种简单的请求 / 响应协议,网络管理系统发出一个请求,管理器则返回一个响应,支持 GET 、 GETNEXT 、 SET 和 TRAP 这 4 种操作;SNMPV2
3、 在 SNMPV1 的基础上还新增了两种新操作,GET BULK 和 INFORM;SNMPV3 中增加了安全管理方式及远程控制。由于SNMPV3 的安全控制设置起来比较麻烦,所以一些被管设备不支持 SNMPV3,而SNMPV1 对于大数据量处理有缺陷,所以大部分被管设备都支持 SNMPV2,应用得比较多的也是 SNMPV2。JDK 提供了对于 UDP 的编程的支持,分别是数据报套接字( DatagramSocket)和数据包(DatagramPacket),直接采用这些类可以实现对 SNMP 协议的操作调用,但是正如西方国采用 Java 进行 SNMP 通信的指南第 3 页/共 14 页家一
4、句谚语“不要重复发明轮子(Dont Reinvent the Wheel) ”,我们没有必要从头开始在设计如何通过 Java 来实现 SNMP,开源源代码的阵营里有好多工具包已经帮我们做掉这件事情了,其中比较出名的有 joeSNMP、SNMP4J 、iReasoning Java SNMP API 等,笔者采用的是 SNMP4J,利用 SNMP4J 可以实现多种 SNMP 的应用,如客户端调用、Trap 的收发、服务端模拟,下面我们就详细讲解如何通过 SNMP4J 来实现客户端及模拟器(服务端)的应用。2.1.SNMP4J 介绍我们用到了 SNMP4J 的几个基础类:org.snmp4j.Tr
5、ansportMapping;org.snmp4j.Snmp;org.snmp4j.smi.Address;org.snmp4j.Target;TransportMapping 类是对传输层的封装,对 UDP 封装为DefaultUdpTransportMapping,TCP 封装为 DefaultTcpTransportMapping,TransportMapping只定义传输的接口,由于 SNMP 默认采用 UDP 作为传输协议,所以笔者感觉DefaultTcpTransportMapping 不会被用到。Snmp 类是 SNMP4J 的核心,它提供了发送和接收 SNMP PDUs 的方法
6、,所有的 SNMP PDU 类型都可以采用同步或者异步的方式被发送。PDU.java implements BERSerializable /SNMPv2 的报文,提供了编码时需要的信息(个人觉得编码信息可以由工具类提供,对用户会混淆)。报文结构参见http:/ PDUv1, ScopedPDU(v3)。AddressIP 地址和端口(和 的不同), 常用实现是 UdpAddress。Target 发送的时候要用到,包含 Address,超时、重试次数、SNMP 的版本,常用实现是 ComunityTarget,可以指定 read community 及 write community。2.
7、2.实现客户端2.2.1. 初始化首先定义类变量,DATATYPE 常量的定义是在 SET 操作里用到的,代码如下:代码:/* TransportMapping */private TransportMapping transport;/* */private Snmp protocol;/* DateType 定義 */* Counter32 */public static final int DATATYPE_COUNTER32 = 0;/* Counter64 */采用 Java 进行 SNMP 通信的指南第 4 页/共 14 页public static final int DATAT
8、YPE_COUNTER64 = 1;/* Gauge32 */public static final int DATATYPE_GAUGE32 = 2;/* GenericAddress */public static final int DATATYPE_GENERICADDRESS = 3;/* Integer32 */public static final int DATATYPE_INTEGER32 = 4;/* IpAddress */public static final int DATATYPE_IPADDRESS = 5;/* OctetString */public stat
9、ic final int DATATYPE_OCTETSTRING = 6;/* TimeTicks */public static final int DATATYPE_TIMETICKS = 7;/* UnsignedInteger32 */public static final int DATATYPE_UNSIGNEDINTEGER32 = 8;代码段 2.2.1 -1初始化,代码如下:代码:/* 初期化* * throws SmsTerminalException* */private void init() throws SmsTerminalException try trans
10、port = new DefaultUdpTransportMapping();protocol = new Snmp(transport); catch (IOException ex) throw new SmsTerminalException(“init error“, Constants.EXIT_CODE_NORMAL, ex);代码段 2.2.1-22.2.2. GET/GETNEXT 操作SnmpVO 是一个简单的 JavaBean,包含了以下属性 ipAddress(目标机器 IP 地址) ; port(端口) ; retry(重试次数) ; timeout(超时时间) ;
11、oid(要获取属性的 OID) ; type(操作类型,默认是 GET) ; communityGet(GET 操作的团体字符串) ; communitySet(SET 操作的团体字符串) ;GET/GETNEXT 操作的代码如下:代码:/*采用 Java 进行 SNMP 通信的指南第 5 页/共 14 页* MIB 情報取得* * param snmpVo* SNMP 相関情報* return outValue* SNMP 取得結果* * throws SmsTerminalException* */public String getSnmpValue(SnmpVO snmpVo) thro
12、ws SmsTerminalException log.debug(“getSnmpValue start“);String outValue = “;try CommunityTarget myTarget = setTarget(snmpVo, false);transport.listen();PDU request = setRequest(snmpVo);PDU response = null; / PDU responseResponseEvent responseEvent = protocol.send(request, myTarget);if (snmpSendReceiv
13、eListener != null) snmpSendReceiveListener.beforeReceive();/ SNMP 受信response = responseEvent.getResponse();if (response != null) if (response.getErrorIndex() = PDU.noErrorif (vb.isException() throw new SmsTerminalException(vb.getVariable().toString(), Constants.EXIT_CODE_NORMAL, responseEvent.getErr
14、or(); outValue = vb.getVariable().toString(); else / 発生throw new SmsTerminalException(responseEvent.getError().getMessage(), Constants.EXIT_CODE_NORMAL, responseEvent.getError(); else / throw new SmsTerminalTimeoutException(“SNMP 受信“); catch (IOException ex) throw new SmsTerminalException(“getSnmpVa
15、lue IO error“, Constants.EXIT_CODE_NORMAL, ex); finally try 采用 Java 进行 SNMP 通信的指南第 6 页/共 14 页if (protocol != null) protocol.close();if (transport != null catch (IOException ex) throw new SmsTerminalException(“close protocol or transport error“, Constants.EXIT_CODE_NORMAL, ex);log.debug(“getSnmpValue
16、 end“);return outValue;代码段 2.2.2-1其中用到了 setTarget 方法,采用 ComunityTarget 设置目标设备 IP 和端口、Community、SNMP 版本、重试次数、超时时间,代码如下:代码:/* CommunityTarget 設定* * param snmpVo* SNMP 相関情報* param isSetOperation SET 操作* return myTarget* 設定 CommunityTarget* */private CommunityTarget setTarget(SnmpVO snmpVo, boolean isSe
17、tOperation) CommunityTarget myTarget = new CommunityTarget();Address deviceAdd;deviceAdd = GenericAddress.parse(snmpVo.getIpAddress() + “/“+ snmpVo.getPort();myTarget.setAddress(deviceAdd); / addressif (isSetOperation) if (StringConverter.nullCheckString(snmpVo.getCommunitySet() myTarget.setCommunit
18、y(new OctetString(snmpVo.getCommunitySet(); / communitySet else if (StringConverter.nullCheckString(snmpVo.getCommunityGet() myTarget.setCommunity(new OctetString(snmpVo.getCommunityGet(); / communityGetif (snmpVo.getRetry() != 0) myTarget.setRetries(snmpVo.getRetry(); / retriesif (snmpVo.getTimeout
19、() != 0) myTarget.setTimeout(snmpVo.getTimeout(); / timeout/ int version = SnmpConstants.ver;if (StringConverter.nullCheckString(snmpVo.getCommunityGet()采用 Java 进行 SNMP 通信的指南第 7 页/共 14 页/ org.snmp4j.mp.*return myTarget;代码段 2.2.2-2还有 setRequest 方法,代码如下:代码:/* OID 設定* * param snmpVo* SNMP 相関情報* return
20、outValue* 設定 VariableBinding* */private PDU setRequest(SnmpVO snmpVo) PDU request = new PDU();VariableBinding var;/ oid 設定var = new VariableBinding(new OID(snmpVo.getOid();request.add(var);/ 情報取得方式設定request.setType(snmpVo.getType();if (snmpVo.getType() = 0) request.setType(PDU.GET); /Default PDU typ
21、e is GET.return request;代码段 2.2.2-32.2.3. SET 操作Set 操作的代码如下:代码:/* MIB 情報設定* author honghui* param snmpVo* SNMP 相関情報* param value* SNMP 設定内容* param dataType * throws SmsTerminalException* */采用 Java 进行 SNMP 通信的指南第 8 页/共 14 页public void setSnmpValue(SnmpVO snmpVo, String value, int dataType) throws Sms
22、TerminalException log.debug(“setSnmpValue start“);try CommunityTarget myTarget = setTarget(snmpVo, true);transport.listen();PDU request = new PDU();OID oid = new OID(snmpVo.getOid();Variable variable = getDataTypeVariable(value, dataType);if (variable = null) throw new SmsTerminalException(“Invalid
23、dataType : “ + dataType,Constants.EXIT_CODE_NORMAL, null);request.add(new VariableBinding(oid, variable);request.setType(PDU.SET);PDU response = null; / PDU responseResponseEvent responseEvent = protocol.send(request, myTarget);if (snmpSendReceiveListener != null) snmpSendReceiveListener.beforeRecei
24、ve();/ SNMP 受信response = responseEvent.getResponse();if (response != null) if (response.getErrorIndex() = PDU.noError else / throw new SmsTerminalTimeoutException(“SNMP 受信“); catch (IOException ex) throw new SmsTerminalException(“setSnmpValue IO error“, Constants.EXIT_CODE_NORMAL, ex); finally try i
25、f (protocol != null) protocol.close();if (transport != null catch (IOException ex) throw new SmsTerminalException(“close protocol or transport error“, Constants.EXIT_CODE_NORMAL, ex);采用 Java 进行 SNMP 通信的指南第 9 页/共 14 页代码段 2.2.3-1Set 操作调用到了 getDataTypeVariable 方法,需要把传入的 dataType 转换成 SNMP4J的 Variable,代码
26、如下:代码:/* Variable 変換* author honghui* param value SNMP 設定内容* param dataType * return Variable*/private Variable getDataTypeVariable(String value, int dataType) switch (dataType) case DATATYPE_COUNTER32:return new Counter32(Long.parseLong(value);case DATATYPE_COUNTER64:return new Counter64(Long.parse
27、Long(value);case DATATYPE_GAUGE32:return new Gauge32(Long.parseLong(value);case DATATYPE_GENERICADDRESS:return new GenericAddress();case DATATYPE_INTEGER32:return new Integer32(Integer.parseInt(value);case DATATYPE_IPADDRESS:return new IpAddress(value);case DATATYPE_OCTETSTRING:return new OctetStrin
28、g(value);case DATATYPE_TIMETICKS:return new TimeTicks(Long.parseLong(value);case DATATYPE_UNSIGNEDINTEGER32:return new UnsignedInteger32(Long.parseLong(value);return null;代码段 2.2.3-22.3.实现模拟器模拟器(或者说是服务端)的作用很明显,就是在服务器上开启一个监听 SNMP 的服务,当有客户端的请求时,返回一些结果。如果模拟器做的很完美的话,客户端连接某个服务器获取或设置 SNMP 信息时,并没有觉察服务器是真实的
29、还是模拟出来的。在我们的运用中,只是简单的模拟了 GET、GETNEXT 和 SET 这 3 个操作,对于GET/GETNEXT 操作来说,请求后返回的数据也是从数据文件里读取到的,数据文件就是一个普通的 CSV 文件,内容如下:数据文件(CSV):OID,MIB 情報“1.3.6.1.2.1.1.6.0“,“Beijing2008“1.3.6.1.2.1.1.7.0“,“Olympic“代码段 2.3-1采用 Java 进行 SNMP 通信的指南第 10 页/共 14 页由于我们只是简单的模拟,所以 GET 和 GETNEXT 操作返回的结果是相同的(如果要做成不同的,就要对 OID 进行排
30、序处理) 。对于 SET 操作来说,没有返回,如果 SET 出错,就会抛出异常。模拟器的代码不多,处理类的全部代码如下:代码:/* CMTS_SNMP 処理* author honghui* version 2008/9/3*/public abstract class CmtsSnmpSimulatorProcessor implements CmtsSnmpSimulatorResponser, Runnable, CommandResponder /* */protected static final SMSLogger logger = SMSLogger.getLogger(Cmts
31、SnmpSimulatorProcessor.class);/* CMTS_SNMP 設定 VO */private CmtsSnmpConfigFileVO cmtsSnmpConfigFileVo;protected String mMyCommunityName = null;protected TransportMapping udpTransportMapping = null;protected Snmp snmp = null;private Object lock = new Object();private boolean bWaiting = false;/* * para
32、m cmtsSnmpConfigFileVo CMTS_SNMP 設定 VO*/ public CmtsSnmpSimulatorProcessor(CmtsSnmpConfigFileVO cmtsSnmpConfigFileVo) this.cmtsSnmpConfigFileVo = cmtsSnmpConfigFileVo;/* 初期化処理* throws SimulatorException 例外*/public void init() throws SimulatorException try udpTransportMapping = new org.snmp4j.transpo
33、rt.DefaultUdpTransportMapping(new UdpAddress(InetAddress.getByName(cmtsSnmpConfigFileVo.getServerIpAddress(), cmtsSnmpConfigFileVo.getServerPort();snmp = new Snmp(udpTransportMapping);snmp.addCommandResponder(this);udpTransportMapping.listen();bWaiting = true; catch (.UnknownHostException ex) throw
34、new SimulatorException(“接続取得失敗 , 知“, ex); catch (java.io.IOException ex) if (ex instanceof SocketTimeoutException) 采用 Java 进行 SNMP 通信的指南第 11 页/共 14 页throw new SimulatorException(“接続 “, ex);throw new SimulatorException(“接続取得失敗 “, ex);/* * throws SimulatorException 例外*/public void close() throws Simul
35、atorException try try if (udpTransportMapping != null) udpTransportMapping.close(); catch (IOException ex) if (ex instanceof SocketTimeoutException) throw new SimulatorException(“ “, ex);throw new SimulatorException(“敗 “, ex);try if (snmp != null) snmp.close(); catch (IOException ex) if (ex instance
36、of SocketTimeoutException) throw new SimulatorException(“ “, ex);throw new SimulatorException(“敗 “, ex); finally bWaiting = false;synchronized (lock) lock.notify();/* */public void run() try synchronized (lock) while (bWaiting) lock.wait(); catch (InterruptedException ex) logger.debug(“InterruptedEx
37、ception“, null, ex); /*采用 Java 进行 SNMP 通信的指南第 12 页/共 14 页* processPdu* param aEvent CommandResponderEvent*/public void processPdu(CommandResponderEvent aEvent) / String communityName = new String(aEvent.getSecurityName();/ logger.debug(“Community name “ + communityName);PDU pdu = aEvent.getPDU();if
38、(pdu = null) logger.debug(“PDU is Null, nothing to do.“);return; String cmdType = CmtsSnmpConst.CMD_TYPE_UNKNOWN;OID oid = pdu.get(0).getOid();String value = pdu.get(0).getVariable().toString();String oidStr = oid.toString();switch (pdu.getType() case org.snmp4j.PDU.GET:cmdType = CmtsSnmpConst.CMD_T
39、YPE_GET;break;case org.snmp4j.PDU.GETNEXT:cmdType = CmtsSnmpConst.CMD_TYPE_GETNEXT;break;case org.snmp4j.PDU.SET:cmdType = CmtsSnmpConst.CMD_TYPE_SET;break;default:logger.error(“Unsupported request for pdu type is “ + pdu.getType();break;logger.info(LogMessageConst.INFO_CMTSSNMP_RECEIVE, new String“
40、=“ + cmdType + “、PDU=“ + pdu.get(0).toString();StatusInformation statusInformation = new StatusInformation();StateReference stateRef = aEvent.getStateReference();try pdu.setType(PDU.RESPONSE);String response = getResponse(cmdType, oidStr, value);Variable rariable = null;if (response = null) rariable
41、 = Null.noSuchObject; else rariable = new OctetString(response);pdu.set(0, new VariableBinding(oid, rariable);logger.info(LogMessageConst.INFO_CMTSSNMP_SEND, new Stringpdu.get(0).toString(), oid.toString(), response);aEvent.getMessageDispatcher().returnResponsePdu(aEvent.getMessageProcessingModel(),
42、aEvent.getSecurityModel(), aEvent.getSecurityName(),aEvent.getSecurityLevel(), pdu,aEvent.getMaxSizeResponsePDU(), stateRef,statusInformation); catch (MessageException ex) logger.error(“Send response error“, null, ex);采用 Java 进行 SNMP 通信的指南第 13 页/共 14 页代码段 2.3-2其中 CmtsSnmpSimulatorResponser 接口定义的代码如下
43、:代码:/* CMTS_SNMP 応答* author honghui* version 2008/9/3*/public interface CmtsSnmpSimulatorResponser /* 返信電文取得* param cmdType 類型* param oid OID* param value 値* return 返信電文*/public String getResponse(String cmdType, String oid, String value);代码段 2.3-33.示例代码代码结构:client+ SnmpManager.java (SNMP 客户端的 Java
44、实现类)Simulator+ CmtsSnmpSimulator.java (SNMP 模拟器的 Java 主类)+CmtsSnmpSimulatorProcessor.java (SNMP 模拟器的 Java 抽象处理类)+CmtsSnmpSimulatorResponser.java (SNMP 模拟器的 Java 命令返回接口类)Snmp.rar4.实用工具MIB 浏览器: 对支持 SNMP 的被管设备如何查看或设置 MIB 库某些属性的值,ireasoning 公司有一个免费的 MIB 浏览器,下载地址如下:http:/ SNMP4J : http:/www.snmp4j.org/2. RFC-1157 (A Simple Network Management Protocol TELNET PROTOCOL SPECIFICATION): http:/www.ietf.org/rfc/rfc1157.txt采用 Java 进行 SNMP 通信的指南第 14 页/共 14 页3. 浅析 SNMP 协议: http:/