How my InputStream's message is never null when it is receiving data periodically?

Viewed 69

I want to have Server - Client relation. Once client gets connected to a server, server sends a message to a client every 1.5 seconds. Server doesn't do anything with messages from client for simplicity. In client's code, I want to use System.in to send messages to server (which don't mean anything) and when I send it, I want to read server's response (all accumulated messages from server). Code is not a snippet, but "real code".

Client:

public class ClientClass {
    public static final String IP_ADDRESS = "127.0.0.1";
    public static final int PORT = 9090;

    public static void main (String[] args) throws IOException {
        Socket socket = new Socket (IP_ADDRESS,PORT);
        PrintWriter output = new PrintWriter (socket.getOutputStream (),true);
        BufferedReader input = new BufferedReader (new InputStreamReader (socket.getInputStream ()));
        BufferedReader keyboard = new BufferedReader (new InputStreamReader (System.in));

        while (true){
            System.out.println ("Enter message you want to send to server:");
            String msg = keyboard.readLine ();
            output.println (msg);

            String serverResponses = input.readLine ();
            while (serverResponses!=null){
                System.out.println (serverResponses);
                serverResponses=input.readLine ();
            }
        }
    }
}

Server:

public class ServerClass {
    public static int PORT = 9090;

    public static void main (String[] args) throws IOException, InterruptedException {
        ServerSocket server = new ServerSocket (PORT);
        Socket client = server.accept ();
        PrintWriter output = new PrintWriter (client.getOutputStream (),true);
        while (true){
            output.println ("hello from server");
            Thread.sleep (1500);
        }
    }
}

//My comment: readLine() method in client is blocking, so I can't read server's messages until I actually read something from keyboard. That is why I have loop to "pick up" all lines from inputStream. I expect that once I read all the lines, variable stringResponses gets null and breaks from inner while loop. Then I thought that I would get a print to console of "enter message you want to send to server" found in outer loop. Unfortunately, my code never breaks out from inner while loop and I want to know why. How is it never null, when I am writing to a stream every 1.5s, meaning there is plenty of time for it to be null and break from loop.

ACTUAL OUTPUT:

Enter message you want to send to server:
hey! //this is what I typed
hello from server
hello from server
hello from server
//these three popped immediately, as they accumulated.
hello from server
hello from server
hello from server
hello from server
//keeps printing "hello from server" every 1.5s and never comes back to "enter your message" -WHY?
1 Answers

readLine returns null at the end of stream. Since the Server Socket isn't closed. It is never null in your inner loop.

Link - https://docs.oracle.com/javase/7/docs/api/java/io/BufferedReader.html#readLine()

A String containing the contents of the line, not including any line-termination characters, or null if the end of the stream has been reached

What do you expect the behaviour when readLine got invoked in a loop on your keyboard object ?

String msg = keyboard.readLine();
while(msg != null)
   msg = keyboard.readLine();

This too will keep on taking inputs from the System.in. The loop would never end.

Solutions

As quoted above from java documentation, readLine method blocks for an input. That is the reason, there is no effect of delaying server response. The method returns null only at the end of the stream.

Solution 1 - Making use of ready method

ready method returns false when the read methods has to wait for an input data. As the Server code is sleeping for 1.5 sec, this method should return false during that time. The downside of this approach is that if Server is not finished with all the response lines on time, then the inner loop where you collect all Server Responses will break.

while (true){
        System.out.println ("Enter message you want to send to server:");
        String msg = keyboard.readLine ();
        output.println (msg);

        String serverResponses = input.readLine ();
        System.out.println (serverResponses);

        while (input.ready()){
           serverResponses=input.readLine ();
           System.out.println (serverResponses);
        }
    }

Solution 2 - Server sends total lines

If the server knows about the total number of lines, then that can be sent as the first line in the stream. Depending upon total lines, client code can read those many lines. The downside of this approach is that an extra line has to be added in the Server response.

while (true){
        System.out.println ("Enter message you want to send to server:");
        String msg = keyboard.readLine ();
        output.println (msg);

        Integer linesCount = Integer.parseInt(input.readLine ());
        for (int i=0; i< linesCount; i++){
            String serverResponses=input.readLine ();
            System.out.println (serverResponses);
            
        }
    }
Related