编辑:现在会弹出GUI(感谢matt,但是当我按下开始按钮时,程序完全冻结,我必须在jGrasp中结束它。
我在使用Java NIO时遇到问题,当我运行服务器代码时,我的GUI不会弹出。
这里是代码:
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);
}
}
我在这里做错了什么?我遵循了this教程,并在进行简单调试(Iterator<SelectionKey> iterator = readyKeys.iterator();
缺少<SelectionKey>
部分)后,我进行了编译,运行和任何操作。这是我编写的全部代码,我不明白自己在做什么错。
您需要分开您的任务。一种是启动gui,另一种是启动服务器。
使一项任务成为服务器。
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;
}
}
}
}
}
然后将其他任务设置为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);
}
现在您的主要方法可以简化为。
public static void main(String[] args) throws Exception{
Server server = new Server();
EventQueue.invokeAndWait( server::startGui );
server.runServer();
}
这样,gui在EDT上启动,而永不结束的服务器循环占用了主线程。另一个小变化。不要扩展JFrame,只需在startGui方法中创建一个JFrame。这样,[[all gui工作就在EDT上完成。
不是,我也删除了您的异常处理,只是使方法抛出异常。这样,您将看到StackTrace。关于您的新问题,为什么GUI被冻结。这是因为您阻止了EDT。您的start()
方法永远不会退出。解决此问题的最简单的方法是。
public void actionPerformed(ActionEvent e) {
if(e.getSource()==start){
new Thread( ()->start();).start();
}
}这将启动一个新线程来运行您的服务器。注意最明显的问题,如果您单击“启动”多次,它将启动多个线程!
SwingWorker
调度对UI的更新。这些更新将在Swing事件分配线程中进行,因此您甚至不必担心创建自己的线程。非阻塞IO,您可以检查通道是否准备好执行某些操作而不会阻塞。如果尚未准备好I / O操作,则线程可以执行其他任务,并稍后再检查。
但是在您的程序中,您编写的代码有效地等待(处于“忙循环”中)操作准备就绪。因为构造函数永远不会返回,所以甚至不会调用setVisible()
。您永远不会给线程一个机会做任何其他工作,包括绘制UI。
此外,对于Java Swing,有一个特殊的线程对UI执行所有更新。当您创建组件或更新它们显示的内容时,必须使用该线程来完成该工作。 Swing提供SwingWorker
类来安排可操作UI的简短任务。您可以通过完成SwingWorker