Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

BufferedReader never ready (Socket programming in Java)

I have socket already declared socket like this:

serverAddr = InetAddress.getByName(this.ip);
socket = new Socket(serverAddr, port);
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

however, the following doesn't work. in.ready() always returns false and if removed the program will freeze at String message = in.readLine();

private void receive() {
        try {
            InputStreamReader isr = new InputStreamReader(socket.getInputStream());
            System.out.println(isr.getEncoding());
            BufferedReader in = new BufferedReader(isr);
            if (in.ready()) {
                String message = in.readLine();
                if (message != null) {
                    if (listener != null) {
                        listener.receiveMessage(ip, message);
                    } else {
                        print("Client recieved: " + message);//
                    }
                }
            }
            in.close();
        } catch (Exception e) {
            print("Error with input stream: " + e);
            disconnect();
        }

    }

How could i solve this?

EDIT:

This is how sending looks like in my server class: out.println(message); out.flush(); This happens in a loop whenever i've put something in message. out is closed after this loop.

like image 731
Sadface Avatar asked May 19 '11 10:05

Sadface


3 Answers

You shouldn't be using ready() like this. The javadoc says this:

"Returns: True if the next read() is guaranteed not to block for input, false otherwise. Note that returning false does not guarantee that the next read will block. "

Your code is implicitly assuming that ready() -> false means that the next read will block. In actual fact, it means the next read might or might not block.

As @EJP says ... just do the read call.


What could i do to prevent a block though? The client will be unable to send anything if it's blocked

If blocking in read is a problem for your application, either use a separate thread to do the reading, or change your code to use NIO channel selectors.

like image 192
Stephen C Avatar answered Sep 30 '22 00:09

Stephen C


Just remove the in.ready() test. It isn't helping you. readLine() will block until there is data available. What else were you planning to do if no data has arrived yet?

like image 20
user207421 Avatar answered Sep 30 '22 00:09

user207421


There are 3 things that come to my mind:

  • You are re-opening the input stream in every receive call, and wrapping it into a BufferedReader. This might read more than a single line into the buffer, and after finishing (closing it), the remaining buffered bytes will no longer be available for subsequent receive calls
  • Did you think about using an own thread for reading the server messages? There it won't harm if it is blocked
  • I have experienced some problems when closing one side of a socket after writing data, and immediately closing it. Sometimes not all of the data was received by the other side, despite flush() and close() calls. Maybe this is also an issue in your situation

Edit:
Smiply keeping the in reference outside of the receive method will not fully solve your problem. You should use a while loop for reading all buffered messages and call the listener for everyone, e.g.:

if (in.ready()) {
    String message;
    while ((message = in.readLine()) != null) {
        // ...
    }
 }

But watch out as the last line might be a partially read message (e.g. 3 and 1/2 messages were buffered). If this is an issue, you could read the messages char-by-char for determining when a line ends, and use a PushbackReader for putting back incomplete messages.

like image 30
king_nak Avatar answered Sep 29 '22 22:09

king_nak