javasocketsobjectinputstream

java.io.EOFException when reading from socket


I have a server and a client set up in this way. I can't find the cause of the EOFException, because it happens randomly. It throws the following exception every time a client connects, but I can't figure out the source of it. It always occurs before it reads what the client has sent. The exception always is at this line:

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

Here is the exception:

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)

Here is my server code:

        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();

Here is my client code:

        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();
    }

What I've Tried

I've tried to fix this issue many times before. I tried to create object output streams. I've switched the order that I initialize the object input stream and object output stream. This is so that the server doesn't get frozen in a deadlock with the client. I have no idea what could be causing this error.


Solution

  • I think I know what is going on here, but I can't be certain because your code is fragmentary, and the symptoms are not well characterized. (The exceptions are unlikely to really be random, for example.)

    First there is one indisputable fact. A one side of a connection sees an EOFException because the other side has closed the network connection. That's what that exception means.

    In your case, the server gets the exception in the ObjectInputStream constructor because the constructor attempts to read an object stream header that the client side never sends ... on that connection.

    Now, the theory. I think I know why. Here is the relevant part of your code (with some bits snipped out for brevity).

        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);
        [...]
    }
    

    Notice that there are two sockets! The first one is created and passed to the shutdown hook. The second one is created and used within send and then closed.

    I think the problem is the first Socket. When that is created, it establishes a connection to the server. The server code will accept it and then attempt to read. The read will block ... since the client side hasn't written anything to that socket. The client will then call send which opens and uses a different Socket.

    Eventually, the client application exits.

    When it exits, the shutdown hook closes the first socket. That causes the server side to see the end of stream ... and triggers the EOFException.


    So how to fix this?

    It rather depends on the "big picture". Is the real client sending a single message to the server, or does it need to reuse the socket to send multiple messages?

    Assuming the former, the solution is simple:

    1. Get rid of the code that creates a socket and passes it to a shutdown hook. As you have written it, it serves no useful purpose.

    2. Rewrite the send method to use try with resources; e.g.

      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);
          }
      }
      

      Note that the above will automatically close the 3 resources in the correct order.