当按下GUI上的X时,Java Chat将停止为将来的客户端工作

时间:2017-05-07 21:42:24

标签: java user-interface server client chat

我遇到了麻烦,我有服务器 - 客户端连接工作,任何数量的客户端都可以连接聊天并相互发送消息。但如果有人通过单击右上角的X退出GUI,一切都会进入****,未来的加入客户端无法做任何事情。

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;

public class ChatC extends JFrame {

//Attributes
private JTextArea msgArea = new JTextArea();
private JTextField input = new JTextField(45);
private JTextArea onlineUsers;
private ArrayList<String> clientNames;
private String clientName = null;
private ObjectOutputStream out;
private Socket client;

/**
 * ChatC constructor
 * Creates an interactive GUI for the user
 * Establishes connection with the server
 */
public ChatC() {

    clientName = JOptionPane.showInputDialog("Enter username:");
    if (clientName == null || clientName.equals("")) clientName = "Guest" + 
    (int) (Math.random() * 200);

    setSize(600, 600);
    setLocationRelativeTo(null);
    setTitle("Welcome to Connect Four chat room,  " + clientName);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setResizable(false);
    setLayout(new BorderLayout());

    JButton sendMsg = new JButton("ENTER");
    getRootPane().setDefaultButton(sendMsg);

    msgArea.setEditable(false);

    JPanel south = new JPanel(new BorderLayout());

    JScrollPane scrollPane = new JScrollPane(msgArea);
    add(scrollPane, BorderLayout.CENTER);

    onlineUsers = new JTextArea();
    onlineUsers.setEditable(false);
    add(onlineUsers, BorderLayout.EAST);
    south.add(input, BorderLayout.WEST);
    south.add(sendMsg, BorderLayout.EAST);
    add(south, BorderLayout.SOUTH);

    JMenu menu = new JMenu("Menu");
    JMenuItem exit = new JMenuItem("Logout");
    menu.add(exit);
    JMenuBar menuBar = new JMenuBar();
    menuBar.add(menu);
    setJMenuBar(menuBar);

    exit.addActionListener(new ActionListener() {

        public void actionPerformed(ActionEvent ae){

            JOptionPane.showMessageDialog(null, "Thanks for playing Connect Four!", "Farewell", JOptionPane.INFORMATION_MESSAGE);

            try{

                out.writeObject("////" + clientName);
                out.flush();

            }
            catch (IOException e1) {}

        }//end of actionPerformed()

    });//end of anonymous ActionListener

    setVisible(true);

    try{

        client = new Socket("localhost", 16789);
        out = new ObjectOutputStream(client.getOutputStream());
        out.writeObject(clientName);

        sendMsg.addActionListener(new ActionListener(){

            public void actionPerformed(ActionEvent ae){

                if (input.getText() != null && !input.getText().equals("")) {
                    try{

                        out.writeObject(input.getText());
                        out.flush();

                    }//end of try
                    catch (IOException ioe){}

                    input.setText(null);

                }//end of if

            }//end of actionPerformed()

        });//end of anonymous ActionListener

    }//end of try
    catch (UnknownHostException uhe){}
    catch (IOException ioe) {}

    ChatCInner chatInner = new ChatCInner(client);
    chatInner.start();

}//end of ChatC()

/**
 * Main method
 * @param args Project arguments array
 */
public static void main(String[] args) {
    new ChatC();
}

/**
 * Method that displays names of users that are currently participating in the chat room
 */
protected void setOnlineUsers(){

    onlineUsers.setText("Users Online: \n");

    for (Object o : clientNames) {

        onlineUsers.append(o + "\n");

    }//end of for each

}//end of setOnlineUsers()

/**
 * Inner class that functions as a thread for each client
 * Handles user's actions on the GUI and communication with the server
 */
private class ChatCInner extends Thread {

    //Attributes
    Object obj;
    ObjectInputStream in;
    Socket clientC;

    /**
     * ChatCInner constructor
     * @param cl Client socket
     */
    public ChatCInner(Socket cl){

        clientC = cl;

    }//end of ChatCInner()

    /**
     * Method that defines what each thread does
     * Handles client interaction(sending messages etc.)
     */
    public void run() {

        try{

            in = new ObjectInputStream(clientC.getInputStream());

            while ( (obj = in.readObject()) != null ) {

                if (obj instanceof String) {

                    msgArea.append((String) obj);
                    msgArea.setCaretPosition(msgArea.getDocument().getLength());

                }//end of if

                if (obj instanceof ArrayList) {

                    clientNames = (ArrayList<String>) obj;

                    if ( (clientNames.indexOf(clientName) == -1) ) {

                        client.close();
                        System.exit(1);

                    }//end of if
                    else setOnlineUsers();

                }//end of if

            }//end of while loop

        }//end of try
        catch (NullPointerException npe) {}
        catch (IOException ioe) {

            JOptionPane.showMessageDialog(null, "Server was shut down... leaving chat room.", "Server malfunctioning",JOptionPane.WARNING_MESSAGE );
            System.exit(1);

        }
        catch (ClassNotFoundException cnfe) {}

    }//end of run()

}//end of ChatInner class

}//end of ChatC class

这是客户

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.*;
import java.util.ArrayList;
import java.util.Vector;

public class ChatS extends JFrame {

//Attributes
private ArrayList<String> clientNames = new ArrayList<>();
private Vector<ObjectOutputStream> outList = new Vector<>();
private Socket client = null;

/**
 * ChatS constructor
 * Build simple GUI with a button to terminate server
 * Establishes connection with clients
 */
public ChatS() {

    setTitle("Server Operational");
    setSize(300, 150);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLocationRelativeTo(null);
    setLayout(new GridLayout(0, 1));

    JButton stopServer = new JButton("Terminate Server");

    try{

        ServerSocket server = new ServerSocket(16789);

        JLabel localhost = new JLabel(InetAddress.getLocalHost().toString());
        JLabel hostName = new JLabel(InetAddress.getByName("localhost").toString());
        add(localhost);
        add(hostName);

        stopServer.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                System.exit(0);

            }//end of actionPerformed()

        });

        add(stopServer);
        setVisible(true);

        while (true) {

            client = server.accept();
            ChatSInner st = new ChatSInner(client);
            st.start();

        }//end of while loop

    }//end of try
    catch(IOException ioe){}

}//end of ChatS()

/**
 * Main method
 * @param args Project arguments array
 */
public static void main(String[] args){ new ChatS(); }

/**
 * Inner class that functions as a thread
 * Handles user interaction on the chat
 */
public class ChatSInner extends Thread {

    //Attributes
    private int index;
    private ObjectInputStream in;
    private ObjectOutputStream out;
    private Socket clientInner;

    /**
     * ChatSInner constructor
     * Handles clients as separate threads
     * @param cl Socket of the client
     */
    public ChatSInner(Socket cl){

        try {

            clientInner = cl;
            in = new ObjectInputStream(clientInner.getInputStream());
            out = new ObjectOutputStream(clientInner.getOutputStream());
            outList.add(out);

        }//end of try
        catch(IOException ioe){}

    }//end of ChatSInner()

    /**
     * Method to broadcasts message to all clients connected to chat
     * @param msg Message that a client is sharing with everyone
     */
    protected void sendMsg(String msg){

        try {

            for (ObjectOutputStream out2 : outList){

                out2.writeObject(msg + "\n");
                out2.reset();

            }//end of for each

        }//end of try
        catch(IOException ioe){}

    }//end of sendMsg()

    /**
     * Method that sends ArrayList of online users back to each client
     */
    protected void sendArrayList(){

        try{

            for (ObjectOutputStream out2 : outList) {

                out2.writeObject(clientNames);
                out2.reset();

            }//end of for each

        }//end of try
        catch(IOException ioe){}

    }//end of sendArrayList()

    /**
     * Method that defines what each thread does
     * Handles client interaction in the chat room (sending messages etc.)
     */
    public void run() {

        String clientName;

        try{

            Object obj;
            clientName = (String) in.readObject();
            clientNames.add(clientName);
            index = clientNames.indexOf(clientName);

            sendMsg(clientName + " has entered the chat room.\n");
            sendArrayList();

            while ((obj = in.readObject()) != null){

                if (obj instanceof String) {

                    if ( (((String) obj).length() > 4 ) && ( (String) obj).substring(0, 4).equals("////") ){

                        sendMsg(clientNames.get(index) + " has logged out of the chat room.\n");
                        clientNames.remove(clientName);
                        sendArrayList();

                    } //end of if
                    else sendMsg(clientName + ": " + obj);

                }//end of if

            }//end of while loop

        } //end of try
        catch(IOException ioe){}
        catch(ClassNotFoundException cnfe) {}

    }//end of run()

}//end of ChatSInner class

}//end of ChatS class

这是服务器

1 个答案:

答案 0 :(得分:0)

首先,通过对try子句的放置位置进行一些简单的更改,防止服务器崩溃。

在服务器中执行以下操作:

protected void sendMsg(String msg){
    for (ObjectOutputStream out2 : outList){
        try {//try moved to this line
            out2.writeObject(msg + "\n");
        }//end of try
        out2.reset();//reset even if an error occurs so that the next message sends smoothly
    catch(IOException ioe){} //catch moved to this line
    }//end of for each
}//end of sendMsg()

而不是:

protected void sendMsg(String msg){
    try {
        for (ObjectOutputStream out2 : outList){
            out2.writeObject(msg + "\n");
            out2.reset();
        }//end of for each
    }//end of try
    catch(IOException ioe){}
}//end of sendMsg()

注意如何移动try以围绕仅发送的消息(而不是for循环),这样如果一条消息无法发送,那么它将跳过它并在for循环中发送下一条消息而不是崩溃。

现在对你的protected void sendArrayList(){方法做同样的事情,移动try / catch只包围这两行,而不是围绕for循环:

out2.writeObject(clientNames);
out2.reset();

还应进行以下更改,以便您不向最近断开连接的客户端发送消息:

                if ( (((String) obj).length() > 4 ) && ( (String) obj).substring(0, 4).equals("////") ){

                    //first copy the users name so we can use it in our message:
                    String disconnectedUser = clientNames.get(index);
                    //now remove the disconnected user before you do anything else:
                    clientNames.remove(clientName);
                    //now you can send your message without causing the error because the disconnected client has been removed from the clientName list:
                    sendMsg(clientNames.get(index) + " has logged out of the chat room.\n");

                    sendArrayList();

                } //end of if