关闭多线程聊天服务器上的客户端

时间:2017-05-23 21:10:42

标签: java multithreading sockets

我目前正在将聊天服务器作为项目工作,需要一些帮助。我有一个多线程聊天服务器,我希望客户端能够关闭他们的聊天窗口而不会崩溃服务器,因为它失去了连接。到现在为止还挺好。我有一个工作gui,当我有一个客户端登录到服务器时,它可以正常关闭窗口,因此关闭clientIOProcessor而不会导致服务器崩溃。 但是,当我有多个客户端登录到服务器并想要关闭其中一个客户端时,就会崩溃

这是ClientIOProcessor:

public class ClientIOProcessor extends Object implements Runnable, InputListener {

    private ObjectInput socketInput;
    private ObjectOutput socketOutput;
    private boolean keepRunning;
    private ClientModelDistributor assignedModelDistributor;
    private MessageImplementation currentMessage;
    private UserHub userHub;

    public ClientIOProcessor(ClientModelDistributor _assignedModelDistributor, ObjectInput _socketInput,
            ObjectOutput _socketOutput) {

        this.assignedModelDistributor = _assignedModelDistributor;
        this.socketInput = _socketInput;
        this.socketOutput = _socketOutput;

        assignedModelDistributor.getInputHandler().registerInputListener(this);
        keepRunning = true;
    }

    public ClientIOProcessor(ClientLoginProcessor clientLoginProcessor, UserHub userHub) {
        assignedModelDistributor = clientLoginProcessor.assignedModelDistributor;
        socketInput = clientLoginProcessor.socketInput;
        socketOutput = clientLoginProcessor.socketOutput;

        assignedModelDistributor.getInputHandler().registerInputListener(this);
        assignedModelDistributor.getIncomingMessageHandler().addIncomingMessageListener(userHub);
        keepRunning = true;
        this.userHub = userHub;
    }

    public void run() {
        while (keepRunning) {
            currentMessage = null;
            currentMessage = buildMessageFromInput();
            if(currentMessage == null) return;

            switch (currentMessage.getMessageType()) {
            case CHATMSG:
                assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
                break;
            case USRJOINED:
                assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
                break;
            case USRLEFT:
                assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
                break;
            case TERMINATE:
                this.terminateConnection();
                break;
            default:
                System.out.println("An error occurred.");
            }
        }
    }

    public void terminateConnection() {
        keepRunning = false;
        assignedModelDistributor.getIncomingMessageHandler().removeIncomingMessageListener(this.userHub);
        assignedModelDistributor.getInputHandler().removeInputListener(this);
        try {
            socketInput.close();
            socketOutput.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
//      System.exit(0);
    }

    private MessageImplementation buildMessageFromInput() {

        MessageImplementation messageSentByServer = null;
        try {
            messageSentByServer = (MessageImplementation) socketInput.readObject();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }

        return messageSentByServer;
    }

    /**
     * This method can be used to send <code>messages</code> to the server.
     * 
     * @param message
     * @throws IOException
     */
    public void send(Message message) throws IOException {

        try {
            socketOutput.writeObject(message);
            socketOutput.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * This method is called whenever the user enters input to this
     * <code>InputListener</code>'s corresponding <code>InputHandler</code>.
     */
    @Override
    public void inputEntered(String input) {

        try {
            send(assignedModelDistributor.buildUserMessage(input));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

clientIO处理器从服务器获取TERMINATE消息,并调用terminateConnection()方法结束Streams。

这是ServerIOProcessor:

public class ServerIOProcessor implements Runnable, IncomingMessageListener {

    private ServerModelDistributor assignedModelDistributor;
    private User assignedUser;
    private boolean keepRunning;
    private ObjectInputStream socketInput;
    private ObjectOutputStream socketOutput;
    private MessageImplementation currentMessage;
    private final String SYSTEM = "System";

    public ServerIOProcessor(User _assignedUser, ObjectOutputStream _socketOutput, ObjectInputStream _socketInput,
            ServerModelDistributor _assignedModelDistributor) {
        assignedUser = _assignedUser;
        socketInput = _socketInput;
        socketOutput = _socketOutput;
        assignedModelDistributor = _assignedModelDistributor;
        keepRunning = true;

        assignedModelDistributor.getIncomingMessageHandler().addIncomingMessageListener(this);
    }

    public void run() {
        while (keepRunning) {
            currentMessage = null;
            currentMessage = buildMessageFromInput();

            switch (currentMessage.getMessageType()) {
            case CHATMSG:
                assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
                break;
            case NEWTASK:
                createNewTask();
                break;
            case TASKASSGNMNT:
                assignTaskToUser();
                break;
            case NEWLEADER:
                determineLeaderOfTeam();
                break;
            case ADDTEAM:
                addTeam();
                break;
            case RMVTEAM:
                removeTeam();
                break;
            case ADDTEAMMEMBER:
                addUserToTeam();
                break;
            case RMVTEAMMEMBER:
                removeUserFromTeam();
                break;
            case JOINTEAM:
                joinTeam();
                break;
            case LEAVETEAM:
                leaveTeam();
                break;
            case EDITDETAILS:
                editUserDetails();
                break;
            case TERMINATE:
                assignedModelDistributor.getIncomingMessageHandler().enterMessage(currentMessage);
                letClientLeaveServer();
                break;
            default:
                send(newSystemMessage(MessageType.ERROR));
                System.out.println("An error occured in one of the server's active IO processes.");
                break;
            }
        }
    }

    private void createNewTask() {

    }

    private void assignTaskToUser() {
        // TODO Auto-generated method stub

    }

    private void determineLeaderOfTeam() {
        // TODO Auto-generated method stub

    }

    private void addTeam() {
        // TODO Auto-generated method stub

    }

    private void removeTeam() {
        // TODO Auto-generated method stub

    }

    private void addUserToTeam() {
        // TODO Auto-generated method stub

    }

    private void removeUserFromTeam() {
        // TODO Auto-generated method stub

    }

    private void joinTeam() {
        // TODO Auto-generated method stub

    }

    private void leaveTeam() {
        // TODO Auto-generated method stub

    }

    private void editUserDetails() {
        // TODO Auto-generated method stub

    }

    private void letClientLeaveServer() {
        // TODO Auto-generated method stub
        assignedModelDistributor.getIncomingMessageHandler().removeIncomingMessageListener(this);
        try {
            socketInput.close();
            socketOutput.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        keepRunning = false;
    }

    private MessageImplementation buildMessageFromInput() {

        MessageImplementation messageSentByUser = null;
        try {
            messageSentByUser = (MessageImplementation) socketInput.readObject();
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }

        return messageSentByUser;
    }

    private MessageImplementation newSystemMessage(MessageType msgtype) {

        return new MessageImplementation(SYSTEM, msgtype);
    }

    //TODO Check expected purpose of this method.
    /**
     * This method is called whenever a <code>Message</code> is entered to the
     * corresponding <code>IncomingMessageHandler</code>.
     */
    @Override
    public void messageEntered(Message message) {
        // assignedModelDistributor.getIncomingMessageHandler().enterMessage(message);
        try {
            socketOutput.writeObject(message);
            socketOutput.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * Writes the given <code>Message</code> to the Socket's
     * <code>ObjectInputStream</code>.
     * 
     * @throws IOException
     */
    void send(Message message) {

        try {
            socketOutput.writeObject(message);
            socketOutput.flush();
        } catch (Exception r) {
            System.out.println("Error!");
            r.printStackTrace();
        }
    }
}

这里的重要部分是letClientLeaveServer()方法,也可以在这里结束流。但是,我认为这是错误的方式,因为我认为这会结束所有登录客户端的输入/输出流。

它自己的ServerSocket在另一个类ConnectionProcessor中初始化,该类在给定端口上启动Socket并接受它。然后connectionProcessor启动serverLogInProcessor,为serverIOProcessor启动一个新线程。

这里是ConnectionProcessor:

public class ConnectionProcessor implements Runnable {

    private ServerModelDistributor assignedModelDistributor;
    private boolean keepRunning;
    private ServerSocket serverSocket;
    private int assignedPort;

    public ConnectionProcessor(int _assignedPort, ServerModelDistributor _assignedModelDistributor) {

        assignedModelDistributor = _assignedModelDistributor;
        assignedPort = _assignedPort;
        keepRunning = true;
        try {
            serverSocket = new ServerSocket(assignedPort);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {

        while (keepRunning) {
            if(serverSocket.isClosed()) {
                try {
                    serverSocket = new ServerSocket(assignedPort);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                Socket socketCreatedForLoginProcess = serverSocket.accept();
                serverSocket.close();

                Thread serverLoginProcessor = new Thread(
                        new ServerLoginProcessor(this, socketCreatedForLoginProcess, assignedModelDistributor),
                        "loginProcess");
                serverLoginProcessor.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

0 个答案:

没有答案