I've written a program which uses multicast to discover peers on a local network and allows file transfer between them. It works, except some process of acquiring files/initializing the transfer thread is ridiculously slow. It hangs for about 10-15 seconds, then begins transferring and completes normally:
Transfer.java
My JFrame GUI class. This was done with Netbeans for convenience, so any generated code won't be posted here.
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.java
Sends out multicast packets and receives responses - modifies the GUI accordingly. This class is where the errors were.
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)); } }
I'm pretty sure the issue is when the FileDrop object within the UI constructor tries to execute the Client SwingWorker using client.execute(), there's a large delay. I can debug it but it doesn't show any issues. Also, I know the issue can't be with my socket.connect() call within Client(), because the print statement immediately before socket.connect() doesn't print until the program has resumes from wherever it's hanging. Any ideas? I'm completely lost.
-- EDIT: All code above works as desired, bugs noted here have been solved. If anyone wants the full source, I'll gladly share it.