D. Müller
D. Müller

Reputation: 3426

Java Socket connection only works in debug mode

I want to implement a Server which listens endless on a specific port to receive data from many clients (never in parallel, only serial). The first thing I tried is to run the server and then launch a few clients in serial (one after the other).

This sounded very easy to implement, but I actually got into the problem, that the code works only when I run it in debug mode with at least one breakpoint in the server code (but the same fault as when running it normally without a breakpoint), very strange to me.

However here is the server code:

public class TaskExecutionServer {
    public TaskExecutionServer(final int port) {
        new Thread() {
            @Override
            public void run() {
                try {
                    int counter = 0;
                    ServerSocket serverSocket = new ServerSocket(port);

                    while(true) {
                        System.out.println("Waiting for client...");
                        Socket socket = serverSocket.accept();
                        System.out.println("Accepted");
                        InputStream inputStream = socket.getInputStream();
                        ObjectInputStream objectStream = new ObjectInputStream(inputStream);
                        while(inputStream.available() > 0 ) {
                            String to = (String)objectStream.readObject();
                            System.out.println(to);
                            System.out.println(++counter);
                        }
                        objectStream.close();
                        inputStream.close();

                        System.out.println("Closing socket");
                        socket.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
    public static void main(String args[]) {
        new TaskExecutionServer(2003);
    }
}

And here the client code:

public class TaskSenderClient {
  public static void main(String args[]){
    try{
        Socket s = new Socket("localhost",2003);
        OutputStream os = s.getOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(os);

        oos.writeObject("test");
        oos.close();
        os.close();
        s.close();
    }catch(Exception e){
        System.out.println("Client exception");
        e.printStackTrace();
    }
  }
}

this is the console output when running in debug mode with breakpoint in the server code row System.out.println("Accepted");:

Waiting for client...
Accepted
test
1
Closing socket
Waiting for client...
Accepted
test
2
Closing socket
Waiting for client...
Accepted
test
3
Closing socket
Waiting for client...

And the output when running in normal mode / without breakpoints in debug-mode:

Waiting for client...
Accepted
test
1
Closing socket
Waiting for client...
Accepted
Closing socket
Waiting for client...
Accepted
Closing socket
Waiting for client...

I don't get any exception... Can someone help? It's my first attempt to re-use a socket connection in java.

EDIT: Checking inputStream.available returns different values

I just added a System.out.println(inputStream.available()); before the while in server code. This prints

EDIT 2: First wait until inputStream.available != 0 This solution also works for me. However, I removed this code snippet here, because checking of available() seems not to be the correct way for that! -> see the solution!

EDIT 3: New server code, which uses NonEmptyInputStream which checks per PushbackInputStream for non-empty streams:

As this uses the EOFException it seems not to be an optimal solution to me, so I also removed this code snippet (instead see solution below). The usage of exceptions in "correct" code is discussed in the comments below...

Upvotes: 1

Views: 2047

Answers (2)

Vampire
Vampire

Reputation: 38669

InputStream.available() can return 0 if there is no data yet, meaning the client didn't send some yet or at least it is not arrived yet. If you add a breakpoint the client has more time to send the data.

You can either add logic like your client first sends how many objects it writes, the server reads the amount and then reads that many objects before it stops reading.

Another possibility would be to insert a PushbackInputStream between the ObjectInputStream and the InputStream and then do a read() on the PushbackInputStream, check the result for -1 which means end-of-stream and if it was not -1, use unread() to push the read byte back into the stream before using the ObjectInputStream methods.

Here you have an example of your originally posted class rewritten with the last pattern:

public class TaskExecutionServer {
    public TaskExecutionServer(final int port) {
        new Thread() {
            @Override
            public void run() {
                try {
                    int counter = 0;
                    ServerSocket serverSocket = new ServerSocket(port);

                    while(true) {
                        System.out.println("Waiting for client...");
                        Socket socket = serverSocket.accept();
                        System.out.println("Accepted");
                        InputStream inputStream = socket.getInputStream();
                        PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
                        ObjectInputStream objectStream = new ObjectInputStream(pushbackInputStream);
                        for(int i; (i = pushbackInputStream.read()) != -1;) {
                            pushbackInputStream.unread(i);
                            String to = (String) objectStream.readObject();
                            System.out.println(to);
                            System.out.println(++counter);
                        }
                        objectStream.close();
                        pushbackInputStream.close();
                        inputStream.close();

                        System.out.println("Closing socket");
                        socket.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    public static void main(String args[]) {
        new TaskExecutionServer(2003);
    }
}

or here again with try-with-resources which is preferable over manually closing AutoClosables.

public class TaskExecutionServer {
    public TaskExecutionServer(final int port) {
        new Thread() {
            @Override
            public void run() {
                try (ServerSocket serverSocket = new ServerSocket(port)) {
                    int counter = 0;

                    while(true) {
                        System.out.println("Waiting for client...");
                        try (Socket socket = serverSocket.accept();
                             InputStream inputStream = socket.getInputStream();
                             PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
                             ObjectInputStream objectStream = new ObjectInputStream(pushbackInputStream)) {
                            System.out.println("Accepted");
                            for(int i; (i = pushbackInputStream.read()) != -1;) {
                                pushbackInputStream.unread(i);
                                String to = (String) objectStream.readObject();
                                System.out.println(to);
                                System.out.println(++counter);
                            }
                            System.out.println("Closing socket");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    public static void main(String args[]) {
        new TaskExecutionServer(2003);
    }
}

Upvotes: 3

user207421
user207421

Reputation: 310916

available() is not a valid test for end of stream. See the Javadoc. You should read from the object stream until EOFException is caught.

Upvotes: 1

Related Questions