客户端与服务器断开连接时,Java套接字写入错误

时间:2019-06-10 11:44:20

标签: java sockets javafx client-server

我正在使用Java聊天应用程序,到目前为止一切正常,除了当客户端断开连接并且其他客户端发送消息时,此错误会弹出:

  java.net.SocketException: Software caused connection abort: socket write error
    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/java.io.DataOutputStream.write(DataOutputStream.java:107)
    at java.base/java.io.DataOutputStream.writeUTF(DataOutputStream.java:401)
    at java.base/java.io.DataOutputStream.writeUTF(DataOutputStream.java:323)
    at com.terkea/com.terkea.system.server.ClientThread.run(ClientThread.java:65)
    at java.base/java.lang.Thread.run(Thread.java:835)

这是我的服务器线程:

@Override
public void run() {
    try {
        DataInputStream in = new DataInputStream(socket.getInputStream());
        DataOutputStream out = new DataOutputStream(socket.getOutputStream());

        while (!socket.isClosed()) {
            try {
                    String input = in.readUTF();
                    if (Message.fromJSON(input).getUserName().equals("REGISTER")) {
                        Message specialMessage = Message.fromJSON(input);
                        specialMessage.setUserName("SERVER");

                        Client test = Client.fromJSON(specialMessage.getMessage());
                        test.setIp(socket.getInetAddress().toString());
                        test.setListening_port(String.valueOf(socket.getPort()));
                        specialMessage.setMessage(Client.toJSON(test));

                        input = Message.toJSON(specialMessage);

                    }
                    for (ClientThread thatClient : server.getClients()) {
                        DataOutputStream outputParticularClient = new DataOutputStream(thatClient.getSocket().getOutputStream());
                        outputParticularClient.writeUTF(input);
                    }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    } catch (IOException e) {
        e.printStackTrace();
    }
}

和客户:

public void createClient() {

    try {
        socket = new Socket(getHost(), portNumber);

        DataInputStream in = new DataInputStream(socket.getInputStream());
        out = new DataOutputStream(socket.getOutputStream());
        Message registerclient = new Message("REGISTER", Client.toJSON(getClient()));
        out.writeUTF(Message.toJSON(registerclient));

        new Thread(() -> {
            while (!socket.isClosed()) {
                try {
                    if (in.available() > 0) {
                        String input = in.readUTF();
                        Message inputMessage = Message.fromJSON(input);
                        if (inputMessage.getUserName().equals("SERVER")) {
                            System.err.println(Client.fromJSON(inputMessage.getMessage()));
                            allClientsConnected.add(Client.fromJSON(inputMessage.getMessage()));
                        } else {
                            chat.add(inputMessage);
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

错误对应于outputParticularClient.writeUTF(input);

我的目标是摆脱此错误,并且如果可能的话,有人可以告诉我一种方法来检查客户端何时断开连接吗?我在这里发现了一些类似的问题,他们的解决方案是检查if (socket.getInputStream().read()!=-1) 但是当我这样做时,整个程序将冻结并且GUI停止工作。

1 个答案:

答案 0 :(得分:1)

您可能想研究扩展特殊消息功能,而不是使用用户名传递“ REGISTER”,而是使用类似messageType的名称。这样,您可以根据类型配置处理程序以执行许多操作。例如:

MessageType { REGISTER, UNREGISTER, READ_RECEIPT, ... }

然后您将拥有以下内容:

RegisterHandler {}
UnregisterHandler{}

并最终将它们扩展为具有一些功能,例如facebook / whatsapp(/ ICQ haha​​):

TypingHandler {} // Other user gets a message saying that I am typing to them

从这里,您可以实现UNREGISTER以执行您想要的操作。就像第一条评论所述,您应该捕获SocketException并手动注销该客户端,以使其不再发生。但您也应该尝试抢先发送

{
    messageType: UNREGISTER,
    from: Client1
    to: server|null,
    data: {}
 }

,以便您的服务器可以在异常发生之前将其删除。如果您对此感兴趣,也可以使用它处理离线消息。