【发布时间】: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。关闭套接字的任何流会关闭其他流和套接字,关闭扩展FilterInputStream或FilterOutputStream的任何内容都会关闭包装的流,您应该关闭最外层的输出流以确保它被刷新,从而关闭其他所有内容. -
注意 (2)
EOFException出现在readObject(),而不是writeObject()。您可能在后者遇到错误,可能是“连接重置”,但绝不是EOFException。 -
您编辑的堆栈跟踪证明了这一点。