当前位置 : 主页 > 编程语言 > java >

snmp4j学习笔记

来源:互联网 收集:自由互联 发布时间:2022-09-29
snmp4j是什么 SNMP4J 是一个用 Java 来实现 SNMP (简单网络管理协议) 协议的开源项目。 官网地址 ​​ https://agentpp.com/api/java/snmp4j.html ​​ 有详细介绍支持哪些特性,这里不再多说。 近期学

snmp4j是什么

        SNMP4J 是一个用 Java 来实现 SNMP (简单网络管理协议) 协议的开源项目。
官网地址 ​​https://agentpp.com/api/java/snmp4j.html​​有详细介绍支持哪些特性,这里不再多说。
近期学习了一下snmp4j,下面通过简单举例(发送一个snmp同步消息)来记录一下snmp4j的使用、关键类、内部工作流程。

Snmp4j如何使用​

Snmp snmp = new Snmp(new DefaultUdpTransportMapping());
snmp.listen();

PDU requestPDU = new PDU();
requestPDU.add(new VariableBinding(new OID("1.3.6.1.2.1.1.1"))); // sysDescr
pdu.setType(PDU.GETNEXT);

CommunityTarget target = new CommunityTarget();
target.setCommunity(new OctetString("public"));
target.setAddress(targetAddress);
target.setVersion(SnmpConstants.version1);

ResponseEvent response = snmp.send(requestPDU, target);
if (response.getResponse() == null) {
// request timed out
}
else {
System.out.println("Received response from: "+
response.getPeerAddress());
// dump response PDU
System.out.println(response.getResponse().toString());
}

关键类

      从上面发送同步消息来自可以看出Snmp是关键类,下面围绕ResponseEvent response = snmp.send(requestPDU, target)语句,画出了内部工作锁涉及的关键类图。
Snmp类实现了CommandResponder接口;
MessageDispatcherImpl实现了MessageDispatcherImpl和TransportListener接口;
DefaultUdpTransportMapping类实现了TransportMapping接口;
PendingRequest类实现了TimerTask接口;
ListenThread是DefaultUdpTransportMapping的内部类,实现了Runable接口。

  • snmp4j学习笔记_java


  • 内部是如何工作​

    下图是ResponseEvent response = snmp.send(requestPDU, target)内部实现简要时序图;

    snmp4j学习笔记_java_02


    ①创建Snmp实例并关联DefaultUdpTransportMapping和MessageDespatcherImpl实例,然后开始监听接收数据流;这是发送消息前必须步骤,举例中是监听所有以太网口的所有端口号:​

    Snmp snmp = new Snmp(new DefaultUdpTransportMapping());​
    snmp.listen();​


    ②send方法内创建了当前消息对应的PendingRequest请求对象,以及创建了SyncResponseListener同步消息应答监听器;

    final SyncResponseListener syncResponse = new SyncResponseListener();​
    PendingRequest retryRequest = null;​
    synchronized (syncResponse) {​
    PduHandle handle = null;​
    PendingRequest request =​
    new PendingRequest(syncResponse, target, pdu, target, transport);​
    request.maxRequestStatus = maxRequestStatus;​
    handle = sendMessage(request.pdu, target, transport, request);​
    try {​
    while ((syncResponse.getResponse() == null) &&​
    (System.nanoTime() < stopTime)) {​
    syncResponse.wait(totalTimeout);​
    }​
    retryRequest = pendingRequests.remove(handle);​
    request.setFinished();​
    request.cancel();​
    }​
    catch (InterruptedException iex) {​
    // cleanup request​
    ...​
    }​
    finally {​
    // free resources​
    ...​
    }​
    }​


    其中PendingRequest有个回调方法pduHandleAssigned,主要是在pendingRequests中放入了PduHandle和PendingRequest的键值对,其中PduHandle中存放了以太网请求包的requestId,以太网响应包中也有requestId用于标识响应哪个请求包,这样请求响应时就可匹配到对应的PendingRequest对象;回调方法中还有个定时器 timerCopy.schedule(this, delay),用于处理超时未响应时的重发。​


    ③MessageDispatcherImpl.sendPdu主要创建了②步中的PduHandle,以及调用DefaultUdpTransportMapping.SendMessage()方法;


    ④DefaultUdpTransportMapping.SendMessage()方法主要是调用java.net.DatagramSocket.send()方法发送UDP报文;​

    DatagramSocket s = ensureSocket();​
    s.send(new DatagramPacket(message, message.length, targetSocketAddress));​


    到这里SNMP报文发送完成,下面是监听接收SNMP-Response报文,并匹配到PendingRequest对象并响应上面的Snmp.send同步方法;


    ⑤ 在DefaultUdpTransportMapping.listen()中,实际是创建了一个线程WorkTask,任务是 DefaultUdpTransportMapping内部类ListenThread的对象;这个线程一直运行监听DatagramSocket.receive(),直到调用Snmp.close()释放资源时停止线程;

    public synchronized void listen() throws IOException {​
    if (listener != null) {​
    throw new SocketException("Port already listening");​
    }​
    ensureSocket();​
    listenerThread = new ListenThread();​
    listener = SNMP4JSettings.getThreadFactory().createWorkerThread(​
    "DefaultUDPTransportMapping_"+getAddress(), listenerThread, true);​
    listener.run();​
    }​


    ⑥在ListenThread内DatagramSocket.receive()到数据流后,调用DefaultUdpTransportMapping.fireProcessMessage,然后调用TransportListener类型监听器MessageDipatcherImpl的processMessage方法,processMessage内再调用自己dispatchMessage方法;

    ⑦dispatchMessage()内先是通过MessageProcessingModel.prepareDataElements类解析数据流,其中MessageProcessingModel

    就是不同版本协议模板(MPv1、MPv2c、MPv3),比如解析数据流中requestId; 然后构建一个CommandResponderEvent对象,再调用fireProcessPdu方​​​​​法,fireProcessPdu()内再调用CommandResponder类型监听器Snmp的processPdu()方法;

    protected void fireProcessPdu(CommandResponderEvent e) {​
    if (commandResponderListeners != null) {​
    List<CommandResponder> listeners = commandResponderListeners;​
    for (CommandResponder listener : listeners) {​
    listener.processPdu(e);​
    // if event is marked as processed the event is not forwarded to​
    // remaining listeners​
    if (e.isProcessed()) {​
    return;​
    }​
    }​
    }​
    }​


    Snmp的processPdu()内根据⑦中解析的PduHandle匹配到PendingRequest,然后调用PendingRequest内注册的Listener监听器(就是②中的SyncResponseListener).onResponse()方法;

    ResponseListener l = request.listener;​
    if (l != null) {​
    l.onResponse(new ResponseEvent(this,​
    event.getPeerAddress(),​
    request.pdu,​
    pdu,​
    request.userObject));​


    SyncResponseListener.onResponse()如下,调用了notify()通知了调用wait()的等待线程(②中syncResponse.wait);

    public synchronized void onResponse(ResponseEvent event) {​
    this.response = event;​
    this.notify();​
    }​


    ⑩ Snmp.send()唤醒继续执行如下, 释放资源,以及返回ResponseEvent

    retryRequest = pendingRequests.remove(handle);​
    if (logger.isDebugEnabled()) {​
    logger.debug("Removed pending request with handle: " + handle);​
    }​
    request.setFinished();​
    request.cancel();​
    ...​
    if (syncResponse.getResponse() == null) {​
    syncResponse.response =​
    new ResponseEvent(Snmp.this, null, pdu, null, null);​
    }​
    return syncResponse.response;​


    上面只是列了主要实现代码,具体细节和其它特性和参考源码。

    【本文来源:香港服务器租用 http://www.558idc.com/st.html欢迎留下您的宝贵建议】
    上一篇:Java高级学习篇之反射
    下一篇:没有了
    网友评论