【问题标题】:Java socket write error when client disconnects from server客户端与服务器断开连接时出现 Java 套接字写入错误
【发布时间】:2019-06-10 11:44:20
【问题描述】:

我正在使用 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 停止工作。

【问题讨论】:

  • 你试过catch(SocketException se){ e.printStackTrace(); }吗?
  • 我刚做了,还是一样。
  • 您收到该错误是因为您正在写入已关闭的套接字。构建一个更优雅的解决方案可能超出了这个问题的范围,但简单地尝试写入并捕获异常,然后自己关闭套接字并不是世界末日。
  • socket.isClosed() 不会像您认为的那样做。特别是,它不会告诉您对等方是否已关闭套接字。

标签: java sockets javafx client-server


【解决方案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: {}
 }

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

【讨论】:

  • 我已经修改了结构,现在我使用处理程序来处理注册和输入,但是当我尝试捕获该异常时,无论有没有那个 catch 语句,我都会得到相同的输出。我做错了吗?
  • 发生异常是因为服务器试图向已关闭的客户端套接字发送数据,没有办法摆脱该异常,只能处理它。 1)如果您遇到该异常,这不一定是坏事,您应该从列表中删除抛出它的客户端 2)您是否在客户端关闭时从客户端发送 UNREGISTER 消息,以便服务器可以将客户端从列表中删除allClientsConnected?
  • 现在,当我尝试删除断开连接的客户端时,我收到此错误:线程“Thread-5”中的异常 java.util.ConcurrentModificationException
  • 我只是整理一下。一切正常,但我还有一个问题,考虑到当我创建一个新的客户端对象时它也会启动一个线程,当客户端决定离开聊天时我怎么可能关闭它?
  • 真的取决于你是怎么做的,但是在你的 ClientThread 你可能想要一个 disconnected() 方法来设置一个标志并允许线程优雅地结束,因为我假设线程是只是在等待数据进入时循环?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-17
相关资源
最近更新 更多