使用Java和SNMP4J实现SNMP操作 以下编写 SnmpUtil 类的教程。该类提供了静态方法来初始化 SNMP、创建 SNMP 目标、创建 PDU、发送 SNMP 请求和处理响应。

引言

SNMP(简单网络管理协议)是一种用于网络设备管理的标准协议。本文将介绍如何使用 Java 和 SNMP4J(一个开源的 SNMP 实现库)进行 SNMP 操作。我们将通过编写 SnmpUtil 类来演示如何进行 SNMP 初始化、创建 PDU、发送 SNMP 请求并处理响应。

环境准备

首先,确保您已经添加了 SNMP4J 依赖。可以通过 Maven 或 Gradle 进行依赖管理。


 org.snmp4j
 snmp4j
 2.8.6
implementation 'org.snmp4j:snmp4j:2.8.6'

SnmpUtil 类概述

以下是编写 SnmpUtil 类的整体结构。该类提供了静态方法来初始化 SNMP、创建 SNMP 目标、创建 PDU、发送 SNMP 请求和处理响应。

类的静态初始化

我们在这使用静态块来初始化 SNMP 对象,确保整个应用程序生命周期中只进行一次初始化。

public class SnmpUtil {
 private static final Logger log = LoggerFactory.getLogger(SnmpUtil.class);
 private static Snmp snmp = null;
 static {
 try {
 initSnmp();
 } catch (IOException e) {
 log.error("SNMP 初始化失败", e);
 }
 }
 private static synchronized void initSnmp() throws IOException {
 if (snmp == null) {
 MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
 messageDispatcher.addMessageProcessingModel(new MPv1());
 messageDispatcher.addMessageProcessingModel(new MPv2c());
 OctetString localEngineID = new OctetString(MPv3.createLocalEngineID());
 USM usm = new USM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID, 0);
 UsmUser user = new UsmUser(new OctetString("SNMPV3"), AuthSHA.ID, new OctetString("authPassword"),
 PrivAES128.ID, new OctetString("privPassword"));
 usm.addUser(user.getSecurityName(), user);
 messageDispatcher.addMessageProcessingModel(new MPv3(usm));
 TransportMapping transportMapping = new DefaultUdpTransportMapping();
 snmp = new Snmp(messageDispatcher, transportMapping);
 snmp.listen();
 }
 }
}

创建目标

编写createTarget 方法用于创建 SNMP 目标,支持 SNMP v1, v2c 和 v3 版本。

private static Target createTarget(int version, String community, String ipAddress, int port) {
 Target target = null;
 if (!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)) {
 log.error("参数version异常");
 return target;
 }
 if (version == SnmpConstants.version3) {
 target = new UserTarget();
 target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
 target.setSecurityName(new OctetString("SNMPV3"));
 } else {
 target = new CommunityTarget();
 ((CommunityTarget) target).setCommunity(new OctetString(community));
 if (version == SnmpConstants.version2c) {
 target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
 }
 }
 target.setVersion(version);
 target.setAddress(GenericAddress.parse("udp:" + ipAddress + "/" + port));
 target.setRetries(5);
 target.setTimeout(3000);
 return target;
}

创建 PDU

编写createPDU 方法用于创建 Protocol Data Unit (PDU),这是 SNMP 消息的基本构成单元。

private static PDU createPDU(int version, int type, String oid) {
 PDU pdu = null;
 if (version == SnmpConstants.version3) {
 pdu = new ScopedPDU();
 } else {
 pdu = new PDUv1();
 }
 pdu.setType(type);
 pdu.add(new VariableBinding(new OID(oid)));
 return pdu;
}
private static PDU createPDU(int version, int type, List oids) {
 PDU pdu = null;
 if (version == SnmpConstants.version3) {
 pdu = new ScopedPDU();
 } else {
 pdu = new PDU();
 }
 pdu.setType(type);
 for (String oid : oids) {
 pdu.add(new VariableBinding(new OID(oid)));
 }
 return pdu;
}

发送 SNMP 请求

编写snmpWalk 方法用于发送 GETNEXT 请求,并处理响应。此处注意,不一定都是用GETNEXT请求,也可以用GET请求,两者区别故名思义,一个是获取下一条oid,一个是获取自己本身

public static PDU snmpWalk(String ipAddr, int port, int version, String community, String oid) {
 try {
 Target target = createTarget(version, community, ipAddr, port);
 PDU pdu = createPDU(version, PDU.GETNEXT, oid);
 ResponseEvent responseEvent = snmp.send(pdu, target);
 PDU response = responseEvent.getResponse();
 return response;
 } catch (IOException e) {
 log.error("SNMP 发送请求失败", e);
 return new PDU();
 }
}
public static PDU snmpWalk(String ipAddr, int port, int version, String community, List oids) {
 try {
 Target target = createTarget(version, community, ipAddr, port);
 PDU pdu = createPDU(version, PDU.GETNEXT, oids);
 ResponseEvent responseEvent = snmp.send(pdu, target);
 PDU response = responseEvent.getResponse();
 return response;
 } catch (IOException e) {
 log.error("SNMP 发送请求失败", e);
 return new PDU();
 }
}

处理 SNMP 响应

编写snmpWalkAll 和 snmpWalkSegment 方法用于处理连续的 SNMP 响应,直到到达 MIB 的末尾。

public static List snmpWalkAll(String ipAddr, int port, int version, String community, String startOid) {
 List results = new ArrayList();
 Target target = createTarget(version, community, ipAddr, port);
 PDU pdu = new PDU();
 pdu.add(new VariableBinding(new OID(startOid)));
 while (true) {
 try {
 pdu.setType(PDU.GETNEXT);
 ResponseEvent responseEvent = snmp.send(pdu, target);
 PDU response = responseEvent.getResponse();
 if (response != null & response.size() > 0 && response.getErrorStatus() == 0) {
 if (response.get(0).getVariable().toString().equals("endOfMibView")) {
 break;
 }
 results.addAll(response.getVariableBindings());
 pdu.setRequestID(null); // Reset request ID for the next request
 pdu.remove(0); // Remove the old request
 pdu.add(response.get(0)); // Add the new request based on the last response
 } else {
 break;
 }
 } catch (IOException e) {
 log.error("SNMP 发送请求失败", e);
 break;
 }
 }
 return results;
}
// ...其他代码见完整代码

完整代码

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.*;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class SnmpUtil {
 private static final Logger log = LoggerFactory.getLogger(SnmpUtil.class);
 private static Snmp snmp = null; // 将共享资源的访问控制为静态初始化
 static {
 // 将 SNMP 对象的初始化放在静态块中,确保只初始化一次
 try {
 initSnmp();
 } catch (IOException e) {
 log.error("SNMP 初始化失败", e); // 记录初始化失败的异常信息
 }
 }
 private static synchronized void initSnmp() throws IOException { // 添加 synchronized 关键字保证线程安全
 if (snmp == null) {
 MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
 messageDispatcher.addMessageProcessingModel(new MPv1());
 messageDispatcher.addMessageProcessingModel(new MPv2c());
 OctetString localEngineID = new OctetString(MPv3.createLocalEngineID());
 USM usm = new USM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID, 0);
 UsmUser user = new UsmUser(new OctetString("SNMPV3"), AuthSHA.ID, new OctetString("authPassword"),
 PrivAES128.ID, new OctetString("privPassword"));
 usm.addUser(user.getSecurityName(), user);
 messageDispatcher.addMessageProcessingModel(new MPv3(usm));
 TransportMapping transportMapping = new DefaultUdpTransportMapping();
 snmp = new Snmp(messageDispatcher, transportMapping);
 snmp.listen();
 }
 }
 private static Target createTarget(int version, String community, String ipAddress, int port) {
 Target target = null;
 if (!(version == SnmpConstants.version3 || version == SnmpConstants.version2c || version == SnmpConstants.version1)) {
 log.error("参数version异常"); // 保持错误日志记录
 return target;
 }
 if (version == SnmpConstants.version3) {
 target = new UserTarget();
 target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
 target.setSecurityName(new OctetString("SNMPV3"));
 } else {
 target = new CommunityTarget();
 ((CommunityTarget) target).setCommunity(new OctetString(community));
 if (version == SnmpConstants.version2c) {
 target.setSecurityModel(SecurityModel.SECURITY_MODEL_SNMPv2c);
 }
 }
 target.setVersion(version);
 target.setAddress(GenericAddress.parse("udp:" + ipAddress + "/" + port));
 target.setRetries(5);
 target.setTimeout(3000);
 return target;
 }
 /**
 * 创建 PDU 对象
 * @param version
 * @param type
 * @param oid
 * @return
 */
 private static PDU createPDU(int version, int type, String oid) {
 PDU pdu = null;
 if (version == SnmpConstants.version3) {
 pdu = new ScopedPDU();
 } else {
 pdu = new PDUv1();
 }
 pdu.setType(type);
 pdu.add(new VariableBinding(new OID(oid)));
 return pdu;
 }
 /**
 * 创建 PDU 对象
 * @param version
 * @param type
 * @param oids
 * @return
 */
 private static PDU createPDU(int version, int type, List oids) {
 PDU pdu = null;
 if (version == SnmpConstants.version3) {
 pdu = new ScopedPDU();
 } else {
 pdu = new PDU();
 }
 pdu.setType(type);
 for (String oid : oids) {
 pdu.add(new VariableBinding(new OID(oid)));
 }
 return pdu;
 }
 /**
 * 获取 SNMP 响应
 * @param ipAddr
 * @param port
 * @param version
 * @param community
 * @param oid
 * @return
 */
 public static PDU snmpWalk(String ipAddr, int port, int version, String community, String oid) {
 try {
 // 由于 snmp 对象已在静态块中初始化,这里不再需要调用 initSnmp
 Target target = createTarget(version, community, ipAddr, port);
 PDU pdu = createPDU(version, PDU.GETNEXT, oid); // 保持内部字符串不变
 ResponseEvent responseEvent = snmp.send(pdu, target);
 PDU response = responseEvent.getResponse();
 return response;
 } catch (IOException e) {
 log.error("SNMP 发送请求失败", e); // 增加异常日志记录
 return new PDU(); // 在异常情况下仍然返回一个新的 PDU 对象,但已记录错误信息
 }
 }
 /**
 * 获取 SNMP 响应
 * @param ipAddr
 * @param port
 * @param version
 * @param community
 * @param oids
 * @return
 */
 public static PDU snmpWalk(String ipAddr, int port, int version, String community, List oids) {
 try {
 // 由于 snmp 对象已在静态块中初始化,这里不再需要调用 initSnmp
 Target target = createTarget(version, community, ipAddr, port);
 PDU pdu = createPDU(version, PDU.GETNEXT, oids); // 保持内部字符串不变
 ResponseEvent responseEvent = snmp.send(pdu, target);
 PDU response = responseEvent.getResponse();
 return response;
 } catch (IOException e) {
 log.error("SNMP 发送请求失败", e); // 增加异常日志记录
 return new PDU(); // 在异常情况下仍然返回一个新的 PDU 对象,但已记录错误信息
 }
 }
 /**
 * 获取 SNMP 响应
 * @param ipAddr
 * @param port
 * @param version
 * @param community
 * @param startOid
 * @return
 */
 public static List snmpWalkAll(String ipAddr, int port, int version, String community, String startOid) {
 List results = new ArrayList();
 Target target = createTarget(version, community, ipAddr, port);
 PDU pdu = new PDU();
 pdu.add(new VariableBinding(new OID(startOid)));
 while (true) {
 try {
 pdu.setType(PDU.GETNEXT);
 ResponseEvent responseEvent = snmp.send(pdu, target);
 PDU response = responseEvent.getResponse();
 if (response != null & response.size() > 0 && response.getErrorStatus() == 0) {
 if (response.get(0).getVariable().toString().equals("endOfMibView")) {
 break;
 }
 results.addAll(response.getVariableBindings());
 pdu.setRequestID(null); // Reset request ID for the next request
 pdu.remove(0); // Remove the old request
 pdu.add(response.get(0)); // Add the new request based on the last response
 } else {
 break;
 }
 } catch (IOException e) {
 log.error("SNMP 发送请求失败", e);
 break;
 }
 }
 return results;
 }
 /**
 * 获取 SNMP 响应
 * @param ipAddr
 * @param port
 * @param version
 * @param community
 * @param startOid
 * @return
 */
 public static List snmpWalkSegment(String ipAddr, int port, int version, String community, String startOid) {
 List results = new ArrayList();
 Target target = createTarget(version, community, ipAddr, port);
 PDU pdu = new PDU();
 pdu.add(new VariableBinding(new OID(startOid)));
 while (true) {
 try {
 pdu.setType(PDU.GETNEXT);
 ResponseEvent responseEvent = snmp.send(pdu, target);
 PDU response = responseEvent.getResponse();
 if (response != null & response.size() > 0 && response.getErrorStatus() == 0) {
 VariableBinding vb = response.get(0);
 results.add(vb);
 // Check if we have reached the end of the branch
 if (!vb.getOid().startsWith(new OID(startOid)) || vb.getVariable().toString().equals("endOfMibView")) {
 break;
 }
 pdu.setRequestID(null); // Reset request ID for the next request
 pdu.remove(0); // Remove the old request
 pdu.add(vb); // Add the new request based on the last response
 System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
 } else {
 break;
 }
 } catch (IOException e) {
 log.error("SNMP 发送请求失败", e);
 break;
 }
 }
 return results;
 }
 public static List snmpWalkSegment(String ipAddr, int port, int version, String community, List startOids) {
 List results = new ArrayList();
 Target target = createTarget(version, community, ipAddr, port);
 for(String startOid: startOids){
 PDU pdu = new PDU();
 pdu.add(new VariableBinding(new OID(startOid)));
 while (true) {
 try {
 pdu.setType(PDU.GETNEXT);
 ResponseEvent responseEvent = snmp.send(pdu, target);
 PDU response = responseEvent.getResponse();
 if (response != null & response.size() > 0 && response.getErrorStatus() == 0) {
 VariableBinding vb = response.get(0);
 results.add(vb);
 // Check if we have reached the end of the branch
 if (!vb.getOid().startsWith(new OID(startOid)) || vb.getVariable().toString().equals("endOfMibView")) {
 break;
 }
 pdu.setRequestID(null); // Reset request ID for the next request
 pdu.remove(0); // Remove the old request
 pdu.add(vb); // Add the new request based on the last response
 System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
 } else {
 break;
 }
 } catch (IOException e) {
 log.error("SNMP 发送请求失败", e);
 break;
 }
 }
 }
 return results;
 }
 public static void main(String[] args) {
 PDU response = SnmpUtil.snmpWalk("127.0.0.1", 161, SnmpConstants.version2c, "public", ".1");
 if (response != null & response.getErrorStatus() == 0) {
 for (VariableBinding vb : response.getVariableBindings()) {
 System.out.println("OID: " + vb.getOid() + ", Value: " + vb.getVariable());
 }
 } else {
 System.out.println("Error in response: " + response.getErrorStatusText());
 }
 }
}

总结

通过本文的讲解,您应该已经掌握了如何使用 Java 和 SNMP4J 库进行 SNMP 操作。我们介绍了 SnmpUtil 类的设计和实现,包括 SNMP 初始化、创建目标、创建 PDU、发送 SNMP 请求和处理响应等内容。希望这篇教程能够帮助您更好地理解和使用 SNMP4J 进行网络设备管理

作者:bigxiamu原文地址:https://blog.csdn.net/bigxiamu/article/details/143163094

%s 个评论

要回复文章请先登录注册