【问题标题】:Java End of File ExceptionJava 文件结束异常
【发布时间】:2019-11-11 09:59:34
【问题描述】:

我正在使用 JavaFX 编写一个简单的即时消息程序,但遇到了文件结束异常,经过数小时的调试,我找不到解决方案。

我的程序分为两部分,客户端和服务器。它们相互发送序列化对象,其中包含完成所需操作的所有必要信息。当我开始在服务器中收到此错误时,我正在向我的程序添加其他功能。此后,我删除了所有附加代码,但仍然收到此错误消息。我不明白为什么我会收到此错误,因为客户端和服务器都没有发送消息,这意味着没有文件可以到达末尾??

我注释掉我调用方法“sendMessage()”的行,这似乎解决了问题,但我需要这个方法才能工作。

下面是服务器类的副本

package instachatfx.server;


public class ServerMain {

    private static HashSet<ObjectOutputStream> writers;
    private static ArrayList<User> users = new ArrayList<>();

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        writers = new HashSet<>();

        try (ServerSocket listener = new ServerSocket(Constants.
            while (true) {
                new Handler(listener.accept()).start();
            }
        } catch (IOException ex) {
            Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private static class Handler extends Thread {

        private Socket socket;

        private ObjectInputStream input;
        private OutputStream os;
        private ObjectOutputStream output;
        private InputStream is;

        private User user;
        private String code;

        public Handler(Socket socket) throws IOException {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                is = socket.getInputStream();
                input = new ObjectInputStream(is);
                os = socket.getOutputStream();
                output = new ObjectOutputStream(os);

                while (socket.isConnected()) {
                    System.out.println("Waiting for packet...");
                    Packet inputmsg = (Packet) input.readObject();
                    if (inputmsg != null) {
                        switch (inputmsg.getHeader()) {
                            case REGISTER:
                                System.out.println("Case: REGISTER");
                                registerUser((User) inputmsg.getContent());
                                break;
                            case LOGIN:
                                System.out.println("Case: LOGIN");
                                loginUser((User) inputmsg.getContent());
                                break;
                            case CODE:
                                System.out.println("Case: CODE");
                                if (((String) inputmsg.getContent()).equalsIgnoreCase(code)) {
                                    Constants.getDBManager().createNewUser(user);
                                    loginUser(user);
                                } else {
                                    sendMessage(PacketHeader.INCORRECT_CODE, "Incorrect code");
                                }
                                break;
                            case MESSAGE:
                                System.out.println("Case: MESSAGE");
                                sendToAll(inputmsg);

                                System.out.println(((Message) inputmsg.getContent()).getUser().toString() + ": " + ((Message) inputmsg.getContent()).getContent());
                                break;
                            default:
                                throw new HeaderNotFoundException("Server has no case for: " + inputmsg.getHeader().name());
                        }
                    }
                }
            } catch (SocketException ex) {
                Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ClassNotFoundException ex) {
                Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            } catch (HeaderNotFoundException ex) {
                Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            } catch (EOFException ex)
                Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IOException ex) {
                Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                closeConnections();
            }

        }

        private void sendToAll(Packet msg) throws IOException {
            System.out.println("Send to all: " + msg.getHeader());
            ((Message) msg.getContent()).setUsers(users);
            for (ObjectOutputStream writer : writers) {
                writer.writeObject(msg);
                writer.flush();
                writer.reset();
            }
        }

        private void sendToOthers(Packet msg) throws IOException {
            System.out.println("Send to others: " + msg.getHeader());
            ((Message) msg.getContent()).setUsers(users);
            for (ObjectOutputStream writer : writers) {
                if (output != writer) {
                    writer.writeObject(msg);
                    writer.flush();
                    writer.reset();
                }
            }
        }

        /**
         * Logs in a user
         * @param u the user to be logged in
         */
        private void loginUser(User u) {
            try {
                User us;
                if ((us = Constants.getDBManager().loginUser(u)) != null) {
                    System.out.println("User found in database");
                    user = us;
                    writers.add(output);
                    users.add(user);
                    LoginMsg msg = new LoginMsg(user, users);
                    sendMessage(PacketHeader.LOGIN_SUCCESSFUL, 
                    sendToOthers(new Packet(PacketHeader.MESSAGE, new Message(user.toString() + " has joined the chat", new User("SERVER", null))));
                } else {
                    sendMessage(PacketHeader.LOGIN_FAIL, "Please check email and password");
                }
            } catch (IOException ex) {
                Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        private void registerUser(User u) throws IOException{
            user = u;
            if (Constants.getDBManager().checkDeuplicateEmail(u.getEmail())) {
                sendMessage(PacketHeader.EMAIL_ALREADY_USED, "The email address " + u.getEmail() + " has already been used to make an account");
                System.out.println("Email already exists");
                return;
            } else if ((code = sendVerificationEmail(u)) == null) {
                sendMessage(PacketHeader.EMAIL_NOT_VALID, "There was an error sending the email  to "
                        + u.getEmail() + ". Please make sure the email is correct");
                System.out.println("email failed sending");
                return;
            }

            sendMessage(PacketHeader.WAIT_FOR_CODE, "Type in code in email");
        }

        /**
         * Sends an email to the user to verify that it is correct
         *
         * @param u The user to send the email to
         * @return null - the email failed sending <br>
         * random sting - the email successfully sent using this verification
         * code
         */
        private String sendVerificationEmail(User u) {
            String code = RandomString.getAlphaNumericString(10);
            if (Constants.getSendEmail().sendEmail(u, code)) {
                return code;
            }

            return null;
        }

        /**
         * Sends a packet to only the client
         *
         * @param packetHeader for the client to tell how to read in the message
         * @param msg the message for the client
         */
        private void sendMessage(PacketHeader packetHeader, Object msg) throws IOException {
            System.out.println("Send to client: " + packetHeader);
            Packet message = new Packet(packetHeader, msg);
            //ERROR OCCURS HERE
            output.writeObject(message);
            output.flush();
        }

        private void closeConnections() {
            if (user != null){
                users.remove(user);
            }

            try {
                sendToAll(new Packet(PacketHeader.MESSAGE, new Message(user.toString() + " has left the chat", new User("SERVER", null))));
            } catch (IOException ex) {
                Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
            }

            if (output != null){
                writers.remove(output);
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {
                    Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            if (os != null){
                try {
                    os.close();
                } catch (IOException ex) {
                    Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            if (input != null) {
                try {
                    input.close();
                } catch (IOException ex) {
                    Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
}

编辑:这里是堆栈跟踪

2019 年 11 月 11 日下午 12:44:21 instachatfx.server.ServerMain$Handler 运行 严重:无 java.io.EOFException 在 java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2950) 在 java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1534) 在 java.io.ObjectInputStream.readObject(ObjectInputStream.java:427) 在 instachatfx.server.ServerMain$Handler.run(ServerMain.java:83)

【问题讨论】:

  • while (socket.isConnected()) 不是有效的测试。它已连接,因为您已连接或接受了它,并且该状态将永远保留在套接字中,即使您亲自关闭它也是如此。它不反映 连接 的状态。这表示为,err,EOFException,表示对等方已关闭连接,或任何其他IOException(读取时除SocketTimeoutException)表示连接已断开,必须关闭套接字。您需要做的就是更改为while (true) 并提前捕获EOFException
  • NB 你只需要关闭output。关闭套接字的任何流会关闭其他流和套接字,关闭扩展 FilterInputStreamFilterOutputStream 的任何内容都会关闭包装的流,您应该关闭最外层的输出流以确保它被刷新,从而关闭其他所有内容.
  • 注意 (2) EOFException 出现在 readObject(),而不是 writeObject()。您可能在后者遇到错误,可能是“连接重置”,但绝不是EOFException
  • 您编辑的堆栈跟踪证明了这一点。

标签: java unexpectendoffile


【解决方案1】:

我不明白为什么会出现此错误...

您收到错误是因为一端已关闭其输出流...或整个套接字。这可能是显式关闭的结果,或者是因为应用程序已退出。

...因为客户端和服务器都没有发送消息...

应用程序(客户端或服务器)正在尝试读取一条消息,但由于另一端已关闭该端试图读取的流,因此不会有任何消息发出。

...表示没有文件可以到达末尾。

你的逻辑不正确。所指的“文件”是上面所指的流。如果关闭写入端,则读取端在尝试继续读取时会看到流结束/文件结束。


看起来一个伪匿名评论者发现了一个可能解释您的问题的错误。 Socket.isConnected() 方法不能用于测试套接字是否仍然连接。 javadoc 声明:

 public boolean isConnected()

返回套接字的连接状态。

注意:关闭套接字并不会清除其连接状态,这意味着 此方法将为关闭的套接字返回 true(请参阅isClosed()),如果 它在关闭之前已成功连接

返回: true 如果套接字成功连接到服务器

(强调...)


如果您需要更多帮助,请提供正确的minimal reproducible example,并确保包含异常的完整堆栈跟踪。

【讨论】:

  • 是的。我以为是这样。但是堆栈跟踪会反驳 OP 关于问题发生位置的理论。
  • 感谢您的帮助。这已经澄清了究竟是什么问题。并感谢您提供指向最小可重现示例的链接。我已经编辑了问题以包含堆栈跟踪,是的,它确实发生在 readObject() 中。你提示客户端程序关闭了连接?
  • 非常感谢!浏览了客户端,发现是在强行关闭连接
  • @JimmyT1601 不,它正在正常关闭连接。如果它“强制”关闭它,您将获得“连接重置”。请不要在任何阶段承认我的意见。
  • @user207421 抱歉,选词不当。我很抱歉我没有承认你!我也实施了您建议的更改。谢谢你:)
猜你喜欢
  • 1970-01-01
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-24
  • 1970-01-01
  • 2018-02-08
相关资源
最近更新 更多