Java SNMP4J陷阱应用程序冻结GUI界面。

4
我有一个Java中的SNMP Trap应用程序,旨在监听SNMP代理并在JFrame窗口中的JTextArea上打印收到的SNMP消息。
下面是源代码,显示了类TrapReceiver的内容。在这个类中,listen方法是完成大部分工作的地方。该类被实例化在一个JFrame类中,在该类中我想要显示消息的JTextArea上。我将JTextArea对象的引用、SNMP代理URL和端口发送到类TrapReceiver的构造函数中,然后调用TrapReceiver对象的run方法,在除JFrame实例之外的单独线程中开始执行。下面的第二部分展示了如何在JFrame实例中实例化TrapReeceiver类。
当我运行应用程序时,我注意到JFrame实例(即GUI)正在冻结,并且在下面所述的JFrame实例中实例化的类TrapReeceiver中没有消息被打印到所提到的JTeaxtArea上。
我的问题是,为什么JFrame实例(即GUI)会冻结,尽管TRapReceiver本身作为单独的线程执行?此外,我想知道可能的解决此冻结问题的可能解决方案。谢谢。
附:我已经验证了TrapReceiver正常工作,并且可以在没有GUI的情况下作为独立应用程序将消息打印到标准输出,但是由于可能存在某些线程同步问题,这个GUI在某种程度上被冻结。 我尝试运行TrapReceiver而不将其放入线程中,在这种情况下,GUI仍然会冻结。 PART I
 package com.[Intenionally removed].snmp;

 import java.io.IOException;
 import javax.swing.JTextArea;
 import org.snmp4j.*;
 import org.snmp4j.mp.MPv1;
 import org.snmp4j.mp.MPv2c;
 import org.snmp4j.security.Priv3DES;
 import org.snmp4j.security.SecurityProtocols;
 import org.snmp4j.smi.OctetString;
 import org.snmp4j.smi.TcpAddress;
 import org.snmp4j.smi.TransportIpAddress;
 import org.snmp4j.smi.UdpAddress;
 import org.snmp4j.transport.AbstractTransportMapping;
 import org.snmp4j.transport.DefaultTcpTransportMapping;
 import org.snmp4j.transport.DefaultUdpTransportMapping;
 import org.snmp4j.util.MultiThreadedMessageDispatcher;
 import org.snmp4j.util.ThreadPool;

 public class TrapReceiver implements CommandResponder, Runnable {

   private String targetSnmpAgentURL;
   private int targetSnmpAgentPort;
   private JTextArea outConsole;

   public TrapReceiver() {
   } 

   public TrapReceiver(JTextArea outConsole) {
       this.outConsole = outConsole;
   }


   public TrapReceiver(JTextArea outConsole, String targetSnmpAgentURL, int  targetSnmpAgentPort) {
       this.targetSnmpAgentURL = targetSnmpAgentURL;
       this.targetSnmpAgentPort = targetSnmpAgentPort;
       this.outConsole = outConsole;

       try {
           listen(new UdpAddress(targetSnmpAgentURL + "/" + targetSnmpAgentPort));
       } catch (IOException e) {
           e.printStackTrace();
       }
    }

    public final synchronized void listen(TransportIpAddress address) throws IOException {


        AbstractTransportMapping transport;
        if (address instanceof TcpAddress) {
            transport = new DefaultTcpTransportMapping((TcpAddress) address);
        } else {
            transport = new DefaultUdpTransportMapping((UdpAddress) address);
        }

        ThreadPool threadPool = ThreadPool.create("DispatcherPool", 10);
        MessageDispatcher mDispathcher = new MultiThreadedMessageDispatcher(
            threadPool, new MessageDispatcherImpl());

        // add message processing models
        mDispathcher.addMessageProcessingModel(new MPv1());
       mDispathcher.addMessageProcessingModel(new MPv2c());

        // add all security protocols
        SecurityProtocols.getInstance().addDefaultProtocols();
        SecurityProtocols.getInstance().addPrivacyProtocol(new Priv3DES());

        // Create Target
        CommunityTarget target = new CommunityTarget();
        target.setCommunity(new OctetString("public"));

        Snmp snmp = new Snmp(mDispathcher, transport);
        snmp.addCommandResponder(this);

        transport.listen();
        System.out.println("Listening on " + address);

        try {
           this.wait();
        } catch (InterruptedException ex) {
           Thread.currentThread().interrupt();
        }
        }

    /**
    * This method will be called whenever a pdu is received on the given port
    * specified in the listen() method
    */
    @Override
    public synchronized void processPdu(CommandResponderEvent cmdRespEvent) {
        //System.out.println("Received PDU...");
        outConsole.append("Received PDU...\n");
        PDU pdu = cmdRespEvent.getPDU();

        if (pdu != null) {            
        outConsole.append("Trap Type = " + pdu.getType() + "\n");
        outConsole.append("Alarm Type: " + pdu.getVariableBindings().get(4) + "\n");
        outConsole.append("Alarm Message:  " + pdu.getVariableBindings().get(9) +  "\n\n");

        }
     }


     @Override
     public void run() {
        try {           
            listen(new UdpAddress(targetSnmpAgentURL + "/" + targetSnmpAgentPort));
        } catch (IOException e) {
            outConsole.append("\nError occured while listening to SNMP messages: \n" + e.getMessage() + "\n\n");
        }
     }

} //end of class TrapReceiver

第二部分

接下来,我在一个线程中运行TrapReceiver类的一个实例。

   private void jButtonStartListeningSNMPActionPerformed(java.awt.event.ActionEvent evt)   {                                                          

    Thread snmpThread = 
           new Thread(new TrapReceiver(jTextAreaSNMPAlarmOutput, jTextFieldSnmpAgentUrl.getText().trim(), Integer.parseInt(jTextFieldSnmpAgentPort.getText().trim())));
    snmpThread.start()

   }
1个回答

5
问题在于你正在TrapReceiver构造函数中调用listen(),这会发生在GUI线程上。你只想在run()方法中调用listen(),因为那是在新线程中执行的部分。请注意,不要删除HTML标签。

似乎我忘了删除它。我刚刚删除了它,但GUI仍然冻结。根据Java教程(位于http://docs.oracle.com/javase/tutorial/uiswing/events/generalrules.html),“因为所有绘图和事件监听方法都在同一个线程中执行,所以慢的事件监听器方法会使程序看起来无响应且重绘速度缓慢。如果您需要执行某些耗时的操作作为事件的结果,请通过启动另一个线程来完成。”我已经在我的应用程序中这样做了,但我不明白为什么它不起作用。 - F. Aydemir
3
嘿,我解决了这个问题。我之前把线程对象发送给了java.awt.inVokeLater方法,但根据我从Java教程中读到的信息,可能只有一个事件分发线程。所以,你的建议帮助了我,是我的错误忘记在构造函数中删除调用。总之,上述"PART II"中的调用是正确的方法。谢谢。 - F. Aydemir
1
抱歉,我复制粘贴太多了,导致我的思维有些混乱,但如果有人需要一个 SNMP trap,上面是可用的代码 :) - F. Aydemir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接