多线程Java文件传输程序在执行SwingWorker之前挂起
我编写了一个程序,它使用多播来发现本地网络上的对等点,并允许它们之间进行文件传输。它可以工作,除了一些获取文件/初始化传输线程的过程慢得可笑之外。它挂起约10-15秒,然后开始传输并正常完成:
转移。爪哇
我的jframegui类。为了方便起见,这是用Netbeans完成的,所以任何生成的代码都不会发布在这里
package transfer; import java.beans.PropertyChangeEvent; import java.io.File; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JFileChooser; import static javax.swing.JFileChooser.FILES_AND_DIRECTORIES; import javax.swing.SwingWorker; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class Transfer extends javax.swing.JFrame { private final FileDrop fileDrop; private Client client; private final Server server; private final ClientMulticast clientMulticast; private final ServerMulticast serverMulticast; public Transfer() throws IOException { initComponents(); this.setTitle("Transfer"); peerBox.setEditable(false); peerBox.removeAllItems(); fileDrop = new FileDrop(backgroundPanel, (java.io.File[] files) -> { System.out.println(files[0].isDirectory()); if (peerBox.getSelectedIndex() != -1) { sendLabel.setText("Hello"); try { client = new Client(sendLabel, files, (Peer) peerBox.getSelectedItem()); //Client property change listener - listens for updates to progress bar client.addPropertyChangeListener((PropertyChangeEvent evt1) -> { if (null != evt1.getPropertyName()) switch (evt1.getPropertyName()) { case "progress": sendProgressBar.setValue((Integer) evt1.getNewValue()); break; } }); client.execute(); } catch (Exception ex) { System.out.println("Unable to send! IOException in FileDrop call."); ex.printStackTrace(System.out); } } else { sendLabel.setText("Host not found"); } }); sendProgressBar.setMaximum(100); sendProgressBar.setMinimum(0); receiveProgressBar.setMaximum(100); receiveProgressBar.setMinimum(0); server = new Server(receiveLabel); //Server property change listener - listens for updates to progress bar server.addPropertyChangeListener((PropertyChangeEvent evt1) -> { if ("progress".equals(evt1.getPropertyName())) { receiveProgressBar.setValue((Integer) evt1.getNewValue()); } }); server.execute(); serverMulticast = new ServerMulticast(); serverMulticast.execute(); clientMulticast = new ClientMulticast(peerBox); clientMulticast.execute(); } ...GENERATED CODE... private void openButtonActionPerformed(java.awt.event.ActionEvent evt) { Transfer guiObject = this; SwingWorker openFile = new SwingWorker<Void, String>() { @Override protected Void doInBackground() throws Exception { openButton.setEnabled(false); fileChooser.setFileSelectionMode(FILES_AND_DIRECTORIES); int returnVal = fileChooser.showOpenDialog(guiObject); if (returnVal == JFileChooser.APPROVE_OPTION && peerBox.getSelectedIndex() != -1) { File[] fileArray = fileChooser.getSelectedFiles(); client = new Client(sendLabel, fileArray, (Peer) peerBox.getSelectedItem()); //Client property change listener - listens for updates to progress bar client.addPropertyChangeListener((PropertyChangeEvent evt1) -> { if ("progress".equals(evt1.getPropertyName())) { sendProgressBar.setValue((Integer) evt1.getNewValue()); } }); client.execute(); //block this swingworker until client worker is done sending while(!client.isDone()) { } } openButton.setEnabled(true); return null; } }; openFile.execute(); } /** * @param args the command line arguments * @throws java.lang.ClassNotFoundException * @throws java.lang.InstantiationException * @throws java.lang.IllegalAccessException * @throws javax.swing.UnsupportedLookAndFeelException */ public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException { System.setProperty("apple.laf.useScreenMenuBar", "true"); System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Transfer"); UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName()); /* Create and display the form */ java.awt.EventQueue.invokeLater(() -> { try { new Transfer().setVisible(true); } catch (IOException ex) { Logger.getLogger(Transfer.class.getName()).log(Level.SEVERE, null, ex); } }); } // Variables declaration - do not modify private javax.swing.JPanel backgroundPanel; private javax.swing.JFileChooser fileChooser; private javax.swing.JButton openButton; private javax.swing.JComboBox<Peer> peerBox; private javax.swing.JLabel receiveHeaderLabel; private javax.swing.JLabel receiveLabel; private javax.swing.JPanel receivePanel; private javax.swing.JProgressBar receiveProgressBar; private javax.swing.JLabel sendHeaderLabel; private javax.swing.JLabel sendLabel; private javax.swing.JPanel sendPanel; private javax.swing.JProgressBar sendProgressBar; // End of variables declaration }
ClientMutlicast。爪哇
发送多播数据包并接收响应-相应地修改GUI。此类是错误所在的位置
package transfer; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import javax.swing.JComboBox; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; public class ClientMulticast extends SwingWorker<Void, Peer> { private boolean peerPreviouslyFound; private byte[] sendData, receiveData; private final DatagramSocket mSocket; private DatagramPacket receivePacket; private final JComboBox<Peer> peerBox; private Peer peer; private ArrayList<Peer> peerList; public ClientMulticast(JComboBox<Peer> peerBox) throws SocketException { peerList = new ArrayList<>(); this.peerBox = peerBox; mSocket = new DatagramSocket(); mSocket.setBroadcast(true); mSocket.setSoTimeout(300); sendData = "CLIENT_MSG".getBytes(); } @Override protected Void doInBackground() throws IOException, InterruptedException, InvocationTargetException { while (true) { try { receiveData = new byte[1024]; receivePacket = new DatagramPacket(receiveData, receiveData.length); peerPreviouslyFound = false; //send broadcast message mSocket.send(new DatagramPacket(sendData, sendData.length, InetAddress.getByName("239.255.255.255"), 8888)); //receive response mSocket.receive(receivePacket); //don't have to worry about responses from local host because //server rejects multicast packets from local host peer = new Peer(receivePacket.getAddress(), receivePacket.getPort(), System.currentTimeMillis()); for (Peer peerList1 : peerList) { if (peerList1.getIPAddress().equals(peer.getIPAddress())) { peerList1.setTimestamp(System.currentTimeMillis()); peerPreviouslyFound = true; break; } } //add to peer list only if reponse is valid, not from local host, and not previously received from this host if ("SERVER_RESPONSE".equalsIgnoreCase(new String(receivePacket.getData()).trim()) && !peerPreviouslyFound) { //publish(peer); peerBox.addItem(peer); peerList.add(peer); } for (int i = 0; i < peerList.size(); i++) { //if peer is greater than 5 seconds old, remove from list if (peerList.get(i).getTimestamp() + 5000 < System.currentTimeMillis()) { peerBox.removeItemAt(i); peerList.remove(i); } } } catch (SocketTimeoutException ex) { for (int i = 0; i < peerList.size(); i++) { //if peer is greater than 5 seconds old, remove from list if (peerList.get(i).getTimestamp() + 5000 < System.currentTimeMillis()) { final int j = i; SwingUtilities.invokeAndWait(() -> { peerBox.removeItemAt(j); peerList.remove(j); }); } } } TimeUnit.MILLISECONDS.sleep(500); }//end while } @Override protected void process(List<Peer> p) { peerBox.addItem(p.get(p.size() - 1)); peerList.add(p.get(p.size() - 1)); } }
我很确定问题在于UI构造函数中的FileDrop对象试图使用客户端执行客户端SwingWorker。execute(),有很大的延迟。我可以调试它,但它没有显示任何问题。另外,我知道问题不可能出在我的插座上。在Client()内调用connect(),因为print语句紧跟在socket之前。connect()在程序从挂起的任何位置恢复之前不会打印。有什么想法吗?我完全迷路了
--编辑:上面的所有代码都可以正常工作,这里提到的错误已经解决。如果有人想要完整的资料来源,我很乐意与大家分享
# 1 楼答案
我正在将名为
peerBox
的组合框传递给我的ClientMuticast
类。我直接修改了这个结构,在其中存储对等点。这阻塞了接口线程,因为ClientMulticast
类一直在访问/更改组合框我更改了代码,使用组合框仅向gui显示值并存储发现的对等点。数组列表用于在每次调用
ClientMulticast
构造函数时复制组合框中的所有值。如果发现对等体,则通过ClientMulticast
类中的publish()
和process()
方法更新组合框。放置在process()
内的任何代码都将计划在EDT上执行