【问题标题】:java.io.EOFException when reading from socket从套接字读取时出现 java.io.EOFException
【发布时间】:2021-05-29 02:21:54
【问题描述】:

我以这种方式设置了服务器和客户端。我找不到 EOFException 的原因,因为它是随机发生的。每次客户端连接时都会引发以下异常,但我无法弄清楚它的来源。它总是在读取客户端发送的内容之前发生。例外总是在这一行:

ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());

这是一个例外:

java.io.EOFException
    at java.base/java.io.ObjectInputStream$PeekInputStream.readFully(ObjectInputStream.java:2860)
    at java.base/java.io.ObjectInputStream$BlockDataInputStream.readShort(ObjectInputStream.java:3355)
    at java.base/java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:939)
    at java.base/java.io.ObjectInputStream.<init>(ObjectInputStream.java:381)
    at com.denesgarda.Socketeer.data.End$3.run(End.java:62)
    at com.denesgarda.Socketeer.data.End$3.run(End.java:76)
    at com.denesgarda.Socketeer.data.End$3.run(End.java:76)
    at com.denesgarda.Socketeer.data.End.listen(End.java:83)
    at Server.<init>(Server.java:10)
    at SStart.main(SStart.java:5)

这是我的服务器代码:

        if(listener == null) this.voidListener();
        ServerSocket serverSocket = new ServerSocket(port);
        End THIS = this;
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                try {
                    Socket socket = serverSocket.accept();
                    socket.setSoTimeout(10000);
                    Connection connection = new Connection(THIS, new End((((InetSocketAddress) socket.getRemoteSocketAddress()).getAddress()).toString().replace("/","")), port, listener);
                    try {
                        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                        Object o = objectInputStream.readObject();
                        if (o.equals("01101100 01101001 01110011 01110100 01100101 01101110 00100000 01110011 01110100 01100001 01110010 01110100")) {
                            listener.event(new ConnectionEvent(connection));
                            listener.event(new ConnectionSuccessfulEvent(connection));
                        }
                        else {
                            listener.event(new ReceivedEvent(connection, o));
                        }
                        socket.close();
                    }
                    catch(EOFException e) {
                        e.printStackTrace();
                    }
                    this.run();
                }
                catch(Exception e) {
                    e.printStackTrace();
                }
            }
        };
        timerTask.run();

这是我的客户端代码:

        if(listener == null) this.voidListener();
        Socket socket = new Socket(address, port);
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        send("Message");



    public void send(Object object) throws IOException {
        Socket socket = new Socket(THAT.getAddress(), this.port);
        OutputStream outputStream = socket.getOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(object);
        socket.close();
    }

我的尝试

我以前曾多次尝试解决此问题。我试图创建对象输出流。我已经切换了初始化对象输入流和对象输出流的顺序。这样服务器就不会与客户端陷入僵局。我不知道是什么导致了这个错误。

【问题讨论】:

  • 您是否尝试将 try-with-resources 与您在代码中创建的所有 AutoClosable 对象一起使用?您的流没有关闭,而关联的套接字已关闭。
  • 在创建任何ObjectOutputStream 之前,对等方已关闭连接。如果客户端已经通过该套接字发送了某些内容,则您不可能在该行获得此异常。可能对等方不是您的客户。这里没有javawebsocket,你也不是在阅读ServerSocket

标签: java sockets objectinputstream


【解决方案1】:

认为我知道这里发生了什么,但我不能确定,因为你的代码是零碎的,而且症状没有很好地表征。 (例如,异常不太可能真的是随机的。)

首先有一个不争的事实。连接的一侧看到EOFException,因为另一侧已关闭网络连接。这就是那个异常的意思。

在您的情况下,服务器在 ObjectInputStream 构造函数中获取异常,因为构造函数尝试读取客户端从未在该连接上发送的对象流标头。

现在,理论。我想我知道为什么。这是您的代码的相关部分(为简洁起见,剪掉了一些位)。

    Socket socket = new Socket(address, port);
    Runtime.getRuntime().addShutdownHook(new Thread() {
        @Override
        public void run() {
            [...]
                socket.close();
            [...]
        }
    });
    send("Message");


public void send(Object object) throws IOException {
    Socket socket = new Socket(THAT.getAddress(), this.port);
    [...]
}

注意有两个套接字!第一个被创建并传递给关闭钩子。第二个在send 中创建和使用,然后关闭。

我认为是第一个Socket的问题。创建后,它会建立与服务器的连接。服务器代码将accept 它然后尝试读取。读取将阻塞......因为客户端没有向 那个 套接字写入任何内容。然后客户端将调用send,它会打开并使用不同的 Socket。

最终,客户端应用程序退出。

当它退出时,shutdown 钩子会关闭 first 套接字。这会导致服务器端看到流的结尾......并触发EOFException


那么如何解决这个问题?

这取决于“大局”。 真正的客户端是向服务器发送一条消息,还是需要复用socket来发送多条消息?

假设是前者,解决方法很简单:

  1. 摆脱创建套接字并将其传递给关闭挂钩的代码。正如你所写,它没有有用的目的。

  2. 重写send 方法以使用资源尝试;例如

    public void send(Object object) throws IOException {
        try (Socket socket = new Socket(THAT.getAddress(), this.port);
             OutputStream os = socket.getOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(os)) {
            oos.writeObject(object);
        }
    }
    

    注意上面会自动按正确的顺序关闭3个资源。

【讨论】:

  • 谢谢!这帮助我解决了这个问题。我所要做的就是使用您的发送方法,并直接通过打开的套接字发送对象!
猜你喜欢
  • 1970-01-01
  • 2015-07-17
  • 1970-01-01
  • 2014-11-09
  • 2022-01-02
  • 1970-01-01
  • 2016-04-30
  • 2018-10-17
  • 1970-01-01
相关资源
最近更新 更多