BufferedReader never ready (Socket programming in Java)
Asked Answered
D

4

7

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.

Dallman answered 19/5, 2011 at 10:42 Comment(2)
Do you call flush() or close() on the server's writer? If not, the message will be buffered and only sent when the buffer is full. Until then, your client won't receive anythingBlanco
Currently i'm calling flush() whenever the server has written anythingDallman
B
1

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.

Blanco answered 19/5, 2011 at 11:9 Comment(2)
I see. Do you suggest i'm keeping the inputreader outside of my recieve-method? Should i also close my output on the server after sending a message? Right now i'm keeping it open until i no longer need it.Dallman
Yes, you should keept the input reader outside of the receive method. On the server side, I meant to not close the socket immediately after writing a big message. So keeping the output stream is perfectly okBlanco
P
4

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.

Paquin answered 19/5, 2011 at 10:58 Comment(1)
I see. What could i do to prevent a block though? The client will be unable to send anything if it's blockedDallman
S
2

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?

Seasonseasonable answered 19/5, 2011 at 10:45 Comment(6)
I'm sending a response from the server, however it's never received for some reason. This client is capable of sending messages, but it can't receive.Dallman
Yes, client is able to send and server successfully receives messages. However, server->client appears unstable. Sometimes the client will just freeze at in.readLine();Dallman
@Dallman - you didn't answer @EJP's (rhetorical) question.Paquin
The problem with the client freezing at in.readLine(); means it's unable to send. It's basically dead. Meaning, i might want to send even if i haven't received anything yet.Dallman
@Dallman the problem with the client freezing at readLine() is that there is no line to read. ready() returning false indicates exactly the same condition. You appear to have a problem with your application protocol. The solution is to fix it.Seasonseasonable
@Dallman fix what? If the sender hasn't sent anything there is nothing to read. You can't fix that, you can only wait.Seasonseasonable
B
1

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.

Blanco answered 19/5, 2011 at 11:9 Comment(2)
I see. Do you suggest i'm keeping the inputreader outside of my recieve-method? Should i also close my output on the server after sending a message? Right now i'm keeping it open until i no longer need it.Dallman
Yes, you should keept the input reader outside of the receive method. On the server side, I meant to not close the socket immediately after writing a big message. So keeping the output stream is perfectly okBlanco
S
0

You may need to call out.flush() to flush anything in BufferedWriter

Simsar answered 19/5, 2011 at 10:49 Comment(2)
in.flush(); apparantly doesn't exist.Dallman
I mean on server side, out.flush() after write something to out.Simsar

© 2022 - 2024 — McMap. All rights reserved.