服务器GUI不一致地冻结,中断或未正确返回

时间:2014-06-13 00:24:23

标签: java swing sockets user-interface

好吧,朋友和我一直在尝试制作我们自己的服务器客户端,并且只需键盘扫描程序和println语句就可以正常工作......直到我们尝试实现gui才开始工作变得怪异......

有时它会起作用......有时它会冻结,有时候什么都不会发生......

我们非常肯定问题出现在ask()方法中,因为它没有发生任何事情,

public String ask(String string)
    {
        println(string);        
        hasInput = false;  

        while(true)
        { 
            //System.out.println("working");                
            if(hasInput)
            {
                println("done");
                return processLastInput();//removes the carot ">" from the input and returns it              
            }
        }        
    }

但是如果你取消注释println语句呢 不一致的工作 ...这是您要查看的其余代码

import java.awt.Color;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;


public class ConsoleGUI
{
    private  JFrame FRAME;
    private  JPanel PANEL;
    private  JTextArea CMD_TEXT;
    private  JTextArea CMD_HISTORY;
    private  JScrollPane CMD_HISTORY_SCROLLER;
    private String LAST_INPUT = "";
    private boolean hasInput = false;   

    private final  int screenX = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
    private final  int screenY = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight(); 

    public ConsoleGUI(String terminalHeader)
    {   
        FRAME = new JFrame(terminalHeader);
        PANEL = new JPanel();
        CMD_TEXT = new JTextArea(">");
        CMD_HISTORY = new JTextArea();
        CMD_HISTORY_SCROLLER = new JScrollPane(CMD_HISTORY_SCROLLER);

        FRAME.setBounds(screenX/2-250,screenY/2-150,500,300);        
        FRAME.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);        
        FRAME.getContentPane().add(PANEL);
        FRAME.setResizable(false);      

        CMD_TEXT.setBackground(Color.BLACK);
        CMD_TEXT.setForeground(Color.GREEN);
        CMD_TEXT.setFont(new Font("courier new",Font.PLAIN,15));
        CMD_TEXT.setBorder(BorderFactory.createTitledBorder("COMMAND:"));
        CMD_TEXT.setBounds(0,220,490,50);        

        CMD_TEXT.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "NEXT");       
        CMD_TEXT.getActionMap().put("NEXT",new ActivateInputAction());

        CMD_HISTORY.setEditable(false);
        CMD_HISTORY.setBackground(Color.BLACK);
        CMD_HISTORY.setForeground(Color.GREEN);
        CMD_HISTORY.setFont(new Font("courier new",Font.PLAIN,20));
        CMD_HISTORY.setBorder(BorderFactory.createTitledBorder("CONSOLE:")); 

        CMD_HISTORY_SCROLLER = new JScrollPane(CMD_HISTORY);
        CMD_HISTORY_SCROLLER.setBounds(0,0,490,220);         

        PANEL.setBackground(Color.GRAY);        
        PANEL.setFocusable(true);
        PANEL.setLayout(null);         
        PANEL.add(CMD_HISTORY_SCROLLER);
        PANEL.add(CMD_TEXT);          

        FRAME.setVisible(true);       
    }    

    public void print(Object ob)
    {
        CMD_HISTORY.append(ob.toString());
    }
    public void println(Object ob)
    {
        CMD_HISTORY.append(ob.toString()+"\n");
    }

    public String getLastInput(){return LAST_INPUT;}
    public String processLastInput()
    {
        String newString = LAST_INPUT.replace(">","");
        return newString;
    }
    public boolean hasInput(){return hasInput;}    

    public String ask(String string)
    {
        println(string);        
        hasInput = false;  

        while(true)
        {                 
            if(hasInput)
            {
                println("done");
                return processLastInput();                
            }
        }        
    }

    class ActivateInputAction extends AbstractAction
    {
        @Override
        public void actionPerformed(ActionEvent e)
        {            
            LAST_INPUT = CMD_TEXT.getText();
            println(LAST_INPUT);
            CMD_TEXT.setText(">");
            CMD_HISTORY.setCaretPosition(CMD_HISTORY.getText().length());  
            hasInput = true;  
        }
    }    
}

如果您愿意,服务器代码也在下面

import java.io.*;
import java.net.*;
import java.util.Scanner;

public class Server 
{
    private static ServerSocket service = null;
    private static String line;
    private static BufferedReader input;
    private static PrintStream output;
    private static Socket clientSocket = null;
    //private static final Scanner keyboard = new Scanner(System.in);
    private static ConsoleGUI Console = new ConsoleGUI("SERVER");

    public static void main(String[] args)
    {
        try
        {                   
            int port = Integer.parseInt(Console.ask("PORT:")); 
            Console.println("PORT = "+port);
            service = new ServerSocket(port);
            Console.println(service.getLocalSocketAddress());
            Console.println("WAITING FOR CLIENT TO CONNECT");            
            clientSocket = service.accept();
            input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            output = new PrintStream(clientSocket.getOutputStream());
            Console.println("CLIENT CONNECTED");
            Console.println(service.getInetAddress());
            while(true)
            {

                line = input.readLine();
                output.println(line);
            }
        }
        catch(Exception e)
        {
            Console.println(e);
        }
    }
}

2 个答案:

答案 0 :(得分:3)

Swing是一个单线程环境,也就是说,期望与UI的所有交互都是从事件调度线程完成的。任何阻止EDT的东西都会阻止UI响应用户或处理新事件

你似乎正在犯这样的错误,试图在EDT的上下文之外更新textarea,这似乎是导致某种线程锁定(至少在Java 8下)

所以,我首先更新了您的print方法,以便在EDT的上下文中更新内容

public void print(final Object ob) {
    Runnable run = new Runnable() {
        @Override
        public void run() {
            CMD_HISTORY.append(ob.toString());
        }
    };
    if (EventQueue.isDispatchThread()) {
        run.run();
    } else {
        EventQueue.invokeLater(run);
    }
}

public void println(Object ob) {
    Runnable run = new Runnable() {
        @Override
        public void run() {
            CMD_HISTORY.append(ob.toString() + "\n");
        }
    };
    if (EventQueue.isDispatchThread()) {
        run.run();
    } else {
        EventQueue.invokeLater(run);
    }
}

我还将您的LAST_INPUThasInput变量更新为volatile ...

private volatile String LAST_INPUT = "";
private volatile boolean hasInput = false;

这将确保它们在线程边界之间正确更新......

不是尝试使用boolean标志作为更多信息的指示器,而是使用对象监视器锁定更好,主要原因是它会使等待线程进入休眠状态,因此它赢了“消耗任何CPU ......

private final Object inputLock = new Object();

//...

public String ask(String string) {
    println(string);
    hasInput = false;

    do {
        synchronized (inputLock) {
            try {
                System.out.println("Wait");
                inputLock.wait();
            } catch (InterruptedException ex) {
            }
        }
    } while (!hasInput);
    println("done");
    return processLastInput();
}

我强烈建议您在继续前进之前阅读Concurrency in Swing。上面的例子充其量只是黑客......

答案 1 :(得分:0)

在ask()中,hasInput初始化为false,因此您将立即进入无限循环。这将导致奇怪的行为,因为它吸收任何可用的周期。