Java NIO still blocking GUI

Petar Zanetic :

Edit: GUI now pops up(thanks to matt ), but when I press the start button, the program completely freezes and I have to end it in jGrasp.

I have an issue with java NIO where my GUI doesn't pop-up when I run the server code.

Here is the code:

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.awt.event.*;

public class Server extends JFrame implements ActionListener{

  JButton start = null;

  public Server(){
     JPanel panel = new JPanel(new FlowLayout());
     start = new JButton("Start");
     add(panel);
     panel.add(start);
     start.addActionListener(this);
  }

  public void start(){
     try{
        Selector selector = Selector.open();

        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);

        InetSocketAddress hostAddress = new InetSocketAddress("localhost", 0);
        serverChannel.bind(hostAddress);

        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
           int readyCount = selector.select();
           if (readyCount == 0) {
              continue;
           }
        // process selected keys...
           Set<SelectionKey> readyKeys = selector.selectedKeys();
           Iterator<SelectionKey> iterator = readyKeys.iterator();
           while (iterator.hasNext()) {
              SelectionKey key = iterator.next();
           // Remove key from set so we don't process it twice
              iterator.remove();
           // operate on the channel...
           // client requires a connection
              if (key.isAcceptable()) {
                 ServerSocketChannel server = (ServerSocketChannel)  key.channel();    
              // get client socket channel
                 SocketChannel client = server.accept();
              // Non Blocking I/O
                 client.configureBlocking(false);
              // record it for read/write operations (Here we have used it for read)
                 client.register(selector, SelectionKey.OP_READ);
                 continue;
              }
           }
        }
     }
     catch(IOException ioe){}
  }

  public void actionPerformed(ActionEvent e) {    
     if(e.getSource()==start){
        start();
     }
  }

  public static void main(String []args){
     Server gui = new Server();
     gui.setTitle("Server");
     gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     gui.pack();
     gui.setLocationRelativeTo(null);
     gui.setResizable(false);
     gui.setVisible(true);
  }
} 

What am I doing wrong here? I followed this tutorial and after simple debugging(Iterator<SelectionKey> iterator = readyKeys.iterator(); was missing the <SelectionKey> part), I compiledit, ran, and...nothing. This is the entire code that I wrote and I don't understand what I'm doing wrong.

matt :

You need to separate your tasks. One is to start a gui, and the other one is to start a server.

Make one task the server.

public void runServer() throws Exception{
        Selector selector = Selector.open();
        ServerSocketChannel serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);

        InetSocketAddress hostAddress = new InetSocketAddress("localhost", 0);
        serverChannel.bind(hostAddress);

        serverChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
           int readyCount = selector.select();
           if (readyCount == 0) {
              continue;
           }
           Set<SelectionKey> readyKeys = selector.selectedKeys();
           Iterator<SelectionKey> iterator = readyKeys.iterator();
           while (iterator.hasNext()) {
              SelectionKey key = iterator.next();
              iterator.remove();
              if (key.isAcceptable()) {
                 ServerSocketChannel server = (ServerSocketChannel)  key.channel();    
              // get client socket channel
                 SocketChannel client = server.accept();
              // Non Blocking I/O
                 client.configureBlocking(false);
              // record it for read/write operations (Here we have used it for read)
                 client.register(selector, SelectionKey.OP_READ);
                 continue;
              }
           }
        }
     }

}

Then make the other task the gui.

public void startGui(){
    JPanel panel = new JPanel(new FlowLayout());
    JButton start = new JButton("Start");
    add(panel);
    panel.add(start);
    setTitle("Server");
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    pack();
    setLocationRelativeTo(null);
    setResizable(false);
    setVisible(true);
}

Now your main method can be reduced to.

public static void main(String[] args) throws Exception{
    Server server = new Server();
    EventQueue.invokeAndWait( server::startGui );
    server.runServer();
}

This way, the gui is started on the EDT, and the server loop which never finishes takes up the main thread. Another small change. Don't extend JFrame, just create a JFrame in the startGui method. That way all gui work is done on the EDT.

Not that I have also removed your Exception handling and just made the methods throw Exception. That way you'll see a StackTrace.

In regards to your newer question, why is the gui freezing. It's because your blocking on the EDT. Your start() method never exits. The most crude method to fix this is to.

public void actionPerformed(ActionEvent e) {    
 if(e.getSource()==start){
      new Thread( ()->start();).start();
 }

}

What that will do, is start a new thread to run your server. Notice the most obvious problem, if you click start more than once, it start more than one thread!

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=477504&siteId=1