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.