无法在单独的线程/窗口中运行类

时间:2018-11-10 11:31:52

标签: java

我正在尝试将多用户聊天客户端Java程序作为另一个Java程序的一部分运行。

如何以可以从主程序打开聊天客户端的方式实现它?我试图使用ProcessBuilder启动它,但它导致整个程序崩溃。

启动聊天客户端的类和客户端客户端本身如下所示

---------------------启动聊天客户端的类---------------------

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

public class ChatCommand extends Command {

    public static final String COMMAND_WORD = "chat";
    public static final String MESSAGE_USAGE = COMMAND_WORD + ":\n" + "Opens up a separate chat programme\n\t"
            + "Example: " + COMMAND_WORD;

    public static final String MESSAGE_SUCCESS = "Initialising chat!";

    public static void main(String[] args) {
        new ChatCommand();
    }

    public ChatCommand() {
        try {
            int result = compile("seedu.addressbook.communications.ChatClient");
            System.out.println("javac returned " + result);
            result = run("seedu.addressbook.communications.ChatClient");
        } catch (IOException | InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    public int run(String clazz) throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder("java", clazz);
        pb.redirectError();
        pb.directory(new File("src"));
        Process p = pb.start();
        InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
        consumer.start();

        int result = p.waitFor();

        consumer.join();

        System.out.println(consumer.getOutput());

        return result;
    }

    public int compile(String file) throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder("javac", file);
        pb.redirectError();
        pb.directory(new File("src"));
        Process p = pb.start();
        InputStreamConsumer consumer = new InputStreamConsumer(p.getInputStream());
        consumer.start();

        int result = p.waitFor();

        consumer.join();

        System.out.println(consumer.getOutput());

        return result;
    }

    public class InputStreamConsumer extends Thread {

        private InputStream is;
        private IOException exp;
        private StringBuilder output;

        public InputStreamConsumer(InputStream is) {
            this.is = is;
        }

        @Override
        public void run() {
            int in = -1;
            output = new StringBuilder(64);
            try {
                while ((in = is.read()) != -1) {
                    output.append((char) in);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
                exp = ex;
            }
        }

        public StringBuilder getOutput() {
            return output;
        }

        public IOException getException() {
            return exp;
        }
    }

    public CommandResult execute() {
        ChatClient cc = new ChatClient();
        try {
            cc.main(new String[]{"a", "b"});
        } catch (Exception e) {
            System.out.println("aaa");
        }
        commandHistory.addHistory(COMMAND_WORD);
        return new CommandResult(MESSAGE_SUCCESS);
    }
}

---------------------------聊天客户端------------------ --------

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

        /* A simple Swing-based client for the chat server.  Graphically
        * it is a frame with a text field for entering messages and a
        * textarea to see the whole dialog.
        *
        * The client follows the Chat Protocol which is as follows.
        * When the server sends "SUBMITNAME" the client replies with the
        * desired screen name.  The server will keep sending "SUBMITNAME"
        * requests as long as the client submits screen names that are
        * already in use.  When the server sends a line beginning
        * with "NAMEACCEPTED" the client is now allowed to start
        * sending the server arbitrary strings to be broadcast to all
        * chatters connected to the server.  When the server sends a
        * line beginning with "MESSAGE " then all characters following
        * this string should be displayed in its message area.
        */

public class ChatClient {

    private BufferedReader in;
    private PrintWriter out;
    private JFrame frame = new JFrame("MediChat");
    private JTextField textField = new JTextField(40);
    private JTextArea messageArea = new JTextArea(8, 40);

     /* Constructs the client by laying out the GUI and registering a
     * listener with the textfield so that pressing Return in the
     * listener sends the textfield contents to the server.  Note
     * however that the textfield is initially NOT editable, and
     * only becomes editable AFTER the client receives the NAMEACCEPTED
     * message from the server.
     */
    public ChatClient() {

        // Layout GUI
        textField.setEditable(false);
        messageArea.setEditable(false);
        frame.getContentPane().add(textField, "North");
        frame.getContentPane().add(new JScrollPane(messageArea), "Center");
        frame.pack();

        // Add Listeners
        textField.addActionListener(new ActionListener() {
            /* Responds to pressing the enter key in the textfield by sending
             * the contents of the text field to the server.    Then clear
             * the text area in preparation for the next message.
             */
            public void actionPerformed(ActionEvent e) {
                out.println(textField.getText());
                textField.setText("");
            }
        });
    }

    /* Prompt for and return the address of the server.
     */
    private String getServerAddress() {
        return JOptionPane.showInputDialog(
                frame,
                "Enter IP Address of the Server:",
                "Welcome to MediChat!",
                JOptionPane.QUESTION_MESSAGE);
    }

    /* Prompt for and return the desired screen name.
            */
    private String getName() {
        return JOptionPane.showInputDialog(
                frame,
                "Choose a screen name:",
                "Screen name selection",
                JOptionPane.PLAIN_MESSAGE);
    }

    /* Connects to the server then enters the processing loop.
     */
    private void run() throws IOException {

        // Make connection and initialize streams
        String serverAddress = getServerAddress();
        Socket socket = new Socket(serverAddress, 9001);
        in = new BufferedReader(new InputStreamReader(
                socket.getInputStream()));
        out = new PrintWriter(socket.getOutputStream(), true);

// Process all messages from server, according to the protocol.
        while (true) {
            String line = in.readLine();
            if (line.startsWith("SUBMITNAME")) {
                out.println(getName());
            } else if (line.startsWith("NAMEACCEPTED")) {
                textField.setEditable(true);
            } else if (line.startsWith("MESSAGE")) {
                messageArea.append(line.substring(8) + "\n");
            }
        }
    }

    /**
     * Runs the client as an application with a closeable frame.
     */
    public static void main(String[] args) throws Exception {
        ChatClient client = new ChatClient();
        client.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        client.frame.setVisible(true);
        client.run();
    }
}

1 个答案:

答案 0 :(得分:1)

您正在使事情复杂化:

  • 请勿将您的客户端代码编译为 any Java类的一部分。例如,在您的IDE或命令行中使用gradle定义项目设置。然后,每当您更改某些内容时,都可以使用它分别编译您的类。在您的类中手动运行javac是严重错误的!
  • 然后,只需确保所有已编译的类文件在jvm的类路径上均可用。不要费心使用反射或其他任何基于类名称的原始字符串。
  • 最重要的是:您可以通过直接实例化对象来使用其他类。仅当要从命令行独立运行类时,才应使用main方法!
  • 因此,通常不用导入类名称作为字符串,而只需导入要使用的类,然后使用 new 创建它们的对象。
  • 除此之外,请分开您的关注点。客户端是客户端,服务器是服务器。让服务器启动客户端实例绝对不是一个好主意。含义:而是创建第三个类(可能称为SetupTestEnvironment),该类首先启动服务器和一些客户端以进行测试。