0

I am using a socket and I am receiving a response from a server which is a string.

The method readline() keeps looping because my response doesn't have a /n or /r at the end of every response. So my program gets stuck at that line.

How do I receive the response, in other words how to tell the readline() method that the transmission is over without having /n or /r at the end of the response? i can not use read() methode because it return an int. herese the reception code

// Get the return message from the server
InputStream is = socket.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);


// lecture de message

String message = br.readLine();

and this is the response that iam suposed to get "162143045875965485214752310109013112500019900008080807812345612345678912500007412589600000000000000000000000000000000000000000000000000000000000000000000000000000000"

Malt
  • 28,965
  • 9
  • 65
  • 105
koutheir
  • 33
  • 7
  • What are you using to read from the socket? a BufferedReader? – Malt Oct 17 '19 at 07:56
  • There is no "readlines()" method in Java SE. And if you really mean `readline()` there are multiple methods with that name. You will need to provide more context if you want help. – Stephen C Oct 17 '19 at 07:56
  • Since `Socket` doesn't have a `readLine()` method I assume you're using a `BufferedReader` or something similar. Since you can't influence what "line" separator that class will use you'd either have to use another reader or roll your own. Besides that, what's the separator for your messages? How can the receiver detect the end of a message? – Thomas Oct 17 '19 at 07:57
  • Possible duplicate of [Reading a byte array from Socket](https://stackoverflow.com/questions/8274966/reading-a-byte-array-from-socket) – Malt Oct 17 '19 at 07:59
  • 1
    @Thomas - *"A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), a carriage return followed immediately by a line feed, or by reaching the end-of-file (EOF)."*. The only explanation is that the OP is trying to read using `readline()` and his "messages" is not line oriented at all. We need to see his code before we can advise him. – Stephen C Oct 17 '19 at 07:59
  • @Malt yes, // Get the return message from the server InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); // lecture de message String message = br.readLine(); – koutheir Oct 17 '19 at 08:00
  • The issue of [end of transmission detection](https://stackoverflow.com/questions/39461018/how-to-detect-end-of-input-stream-from-socket-using-bufferedinputstream) may be related too. – Curiosa Globunznik Oct 17 '19 at 08:01
  • How are you writing the stuff at the other end? – Stephen C Oct 17 '19 at 08:01
  • yes i'am using a BufferedReader , but in trhe context of a tcp/ip socket – koutheir Oct 17 '19 at 08:02
  • Hello koutheir and welcome to Stack Overflow. Your question is a bit too vague to be answered. Please have a look on how to create a [minimal and reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of your problem. You may also want to include a small example response text into your question. Which helps others to understand exactly what the problem is. Otherwise I am afraid your question will be closed for being too broad. – GameDroids Oct 17 '19 at 08:02
  • @Thomas, that's my question, how to detect the end of the message if my response don't have a /n at the end – koutheir Oct 17 '19 at 08:04
  • Might be an ugly solution but you could check for "\n" presence (https://stackoverflow.com/questions/5518451/check-if-string-contains-n-java), and if not, concatenate a line separator at the end ? – Benoit F Oct 17 '19 at 08:04
  • @StephenC, i don't have the hande on the server side but i'am receivin a String of 156 integer like this "162143045875965485214752310109013112500019900008080807812345612345678912500007412589600000000000000000000000000000000000000000000000000000000000000000000000000000000" – koutheir Oct 17 '19 at 08:06
  • Lines are line-terminated. If you aren't receiving line terminators, you aren't receiving lines, so you can't use `readLine()`. Surely this is obvious? What you *can* use depends entirely on what data is being sent, whcih you haven't stated. Possibly you can't do any better than calling `read()` and figuring it all out yourself. – user207421 Oct 17 '19 at 08:43
  • "how to detect the end of the message" - well you should at least know how you could detect it in theory. If you just get data but no signal that the sender is finished then you'd wait indefinitely. There are multiple ways to break that: 1) send the length of the data upfront, i.e. as some form of header info 2) use some character or sequence to mark the end of a message and look for that 3) set some timeout and stop waiting if no more data comes, then try to make do with what you got so far. – Thomas Oct 18 '19 at 07:59

3 Answers3

1

TCP is a stream protocol, which means that bytes are coming from the socket endlessly as long as the socket is connected. The data is therefore not divided into individual messages. Thus, reading from a TCP socket is very similar to reading from a binary file - you can't just read "line by line" since it's all just a blob of data with a start and and end.

If the input doesn't contain delimiters such as \n (or some other character or sequence of bytes) between messages, then there has to be some other way of determining how many bytes to read. There are several approaches to this, depending on the protocol. For instance, in HTTP, responses typically have Content-Length header so that the reader knows when this response ends and the next one begins.

If you're implementing your own protocol, a simple approach is to prefix each message with an int specifying the number of bytes it contains. In that case, all the reader has to do is to read the int, read the appropriate number of bytes from the socket, parse the message, then read the next int...

Another approach is to use fixed-sized messages, and to simply read a fixed number of bytes every time. A third approach is to use a delimiter, like \n or some other sequence of bytes that does not appear in the payload of your protocol.

If you know the number of bytes you want to read, first create a buffer to write the message to. Say we want to read exactly 500 bytes. Allocate the message buffer:

byte messageBuffer[] = new byte[500];

Now we need to read from the socket until one of two conditions are met:

  • There are 500 bytes in the message buffer
  • Or the socket is closed

Conveniently, each time we call read on the InputStream of the socket, we get the number of bytes we've read, or -1 if the stream has ended. Hence, we can read into our message buffer until we either fill it up with 500 bytes, or we get -1 from the read() call.

We end up with a loop like this:

int bytesToRead = 500;
InputStream in = socket.getInputStream();
byte messageBuffer[] = new byte[bytesToRead];
for (int readOffset = 0, readBytes = 0; (readBytes = in.read(messageBuffer, readOffset, messageBuffer.length - readOffset)) != -1
        && readOffset < bytesToRead;) {
    readOffset += readBytes;
}

Or, if you prefer, like this:

int readBytes = 0;
int readOffset = 0;
while (true) {
    readBytes = in.read(messageBuffer, readOffset, messageBuffer.length - readOffset);
    if (readBytes == -1) {
        break;
    }
    readOffset += readBytes;
}

Note I haven't tested this code.

Once you've read enough bytes into the buffer, if you want to make a String out of them, just use new String(messageBuffer) or something like new String(messageBuffer, Charset.forName("UTF-8")) if you want to specify a non-default charset.

Malt
  • 28,965
  • 9
  • 65
  • 105
0

I assume that you see this problem doing something like

String hostName = args[0];
int portNumber = Integer.parseInt(args[1]);

try (
    Socket socket = new Socket(hostName, portNumber);
    BufferedReader in =
        new BufferedReader(
            new InputStreamReader(socket.getInputStream()));
    String line = in.readLine();
)
...

According to Javadoc for BufferedReader#readLine

A line is considered to be terminated by any one of a line feed ('\n'), a carriage return ('\r'), a carriage return followed immediately by a line feed, or by reaching the end-of-file (EOF).

So this issue is expected. I suggest to start using line termination characters. If you can't do that, you can use BufferedReader#read method and parse received stuff manually according your rules.

Sergey Prokofiev
  • 1,815
  • 1
  • 12
  • 21
0

Your problem seems to be that you are trying to use readline() to read data that is not line oriented. If the stream consists of a sequences of messages and you need to read them one at a time ... it won't work.

What do you do?

It depends on what the actual message structure is. For example:

  • If each message always consists of exactly N characters:

    StringBuilder sb = new StringBuilder(N);
    for (int i = 0; i < N; i++) {
        int ch = br.read();
        if (ch == -1) { 
             throw new IOException("truncated message");
        }
        sb.append((char) ch));
    }
    return sb.toString();
    
  • If each message starts with a size:

    // read the size
    // read 'size' characters.
    
  • If each message is terminated by a character C (or end of stream)

    StringBuilder sb = new StringBuilder();
    int ch;
    while((ch = br.read()) != C) {
       sb.append((char) ch));
    }
    return sb.toString();
    

And so on.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216