1

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

  • always 7 in debug-mode with breakpoint
  • 7 (in first run) and 0 (in all other attemps) afterwards in non-debug mode / without breakpoints

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...

8
  • Oh yes! You were completely right (comment seems to be removed again already)... It really seems that the Server code is "too fast" and checks for available data too early! I just added a Thread.sleep(100) after the accept method and everything seems to work fine! Thank you, this saved my day! Commented Aug 24, 2016 at 13:29
  • No, don't do that, that is veeery bad practice and also very unreliable. Just do it the correct way like I described below Commented Aug 24, 2016 at 13:33
  • Do you have to use java.io? Why don't you use the java.nio approach. There are many tutorials on the web. Commented Aug 24, 2016 at 13:37
  • @D.Müller a fast server is generally a good thing. Get the coding right, instead of adding some arbitrary delay. Commented Aug 24, 2016 at 13:40
  • Can you check my "Edit 2" in the question? this works fine for me, but I'm not sure whether this is how to do it right. Commented Aug 24, 2016 at 14:20

2 Answers 2

3

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);
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Great that works now for me, thank you so much! Before I had a problem using the PushbackInputStream and it's unread method (I alsways got an exception because of a wrong / invalid header.
1

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

8 Comments

Well, my available call even doesn't return 0 all the time, only when I run further client requests (and them not in debug mode)...
OK, so you're not calling it on the object input stream. It is still incorrect technique. It doesn't do what you're using it for,
@D.Müller look at my answer, it explains why available() is the wrong method to use. JavaDoc tells it also of course.
@EJP waiting for EOFException is also not the best technique, as exceptions for flow control are bad practice and slow as hell, due to the exception mechnism kicking in. Testing for EOS instead is indeed the right technique if done correctly. ;-)
No need to downvote a perfectly valid answer though. It is a fact that exception mechanism needs a lot of performance and that it is always better to do up-front checks instead of waiting for exceptions. It also is not only the exception throwing, placing the code in a try-catch block also prevents various optimizations the compiler could do otherwise. If you want more reasoning and numbers, you could e. g. read "Effective Java" as even Joshua Bloch says: exceptions are, as their name implies, to be used only for exceptional conditions; they should never be used for ordinary control flow.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.