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

客户端与服务器断开连接时发生Java套接字写入错误,java,sockets,javafx,client-server,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.SocketO

我正在用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();
    }
}
该错误对应于OutputSpecialClient.writeUTFinput

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

您可能需要考虑扩展您的特殊消息功能,而不是使用用户名来传递注册表,而是使用messageType之类的东西来实现这一点。通过这种方式,您可以根据类型配置处理程序,以执行许多操作。例如:

MessageType { REGISTER, UNREGISTER, READ_RECEIPT, ... }
RegisterHandler {}
UnregisterHandler{}
然后,您可以拥有以下内容:

MessageType { REGISTER, UNREGISTER, READ_RECEIPT, ... }
RegisterHandler {}
UnregisterHandler{}
并最终将其扩展为一些功能,如facebook/whatsapp/ICQ哈哈:

TypingHandler {} // Other user gets a message saying that I am typing to them
从这里,您可以实现取消注册以执行您想要的操作。正如第一条评论所说,您应该捕获SocketException并手动注销该客户端,这样它就不会再发生了。但你也应该尝试先发制人地发送

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

以便您的服务器可以在异常发生之前删除它。如果您感兴趣的话,这也可以让您处理脱机消息。

您尝试过catchSocketException se{e.printStackTrace;}吗?我刚刚尝试过,错误也是一样的。您之所以会出现此错误,是因为您正在向关闭的套接字写入。设计一个更优雅的解决方案可能超出了这个问题的范围,但简单地尝试写并捕获异常,然后自己关闭套接字并不是世界末日。socket.isClosed不会做您认为它会做的事情。特别是,它不会告诉您对等方是否关闭了套接字。我已经修改了结构,现在我使用处理程序来处理寄存器和键入,但是当我尝试捕获该异常时,无论是否使用catch语句,我都会得到相同的输出。我做错了什么吗?发生异常是因为服务器试图将数据发送到已关闭的客户端套接字,无法消除该异常,只有处理它。1如果你得到了这个例外,那不一定是坏事,您应该从列表中删除抛出它的客户端2您是否在客户端关闭时从客户端发送注销消息,以便服务器可以从allClientsConnected中删除该客户端?现在,当我尝试删除断开连接的客户端时,会出现以下错误:thread thread-5 java.util.ConcurrentModificationExceptionI中出现异常解决它。一切正常,但我还有一个问题,考虑到当我创建一个新的客户端对象时,它也会启动一个线程,当客户端决定离开聊天室时,我怎么可能关闭它?这取决于你怎么做,但是在ClientThread中,您可能希望有一个断开连接的方法,该方法设置一个标志并允许线程优雅地结束,因为我假设线程只是在等待数据进入时循环?