Most efficient way to read in tcp stream in Java
I need to change the client side code because the comms protocol was not defined correctly.
I assumed the tcp message from the server would end on a new line, so I used reader.readLine () to read my data.
Now I was told that this is not the case and instead the first 4 characters of the message are the length of the message and then I have to read the rest of the message.
What's the most efficient sane way to do this?
my general idea was as follows:
- Create char array
- Read first 4 characters
- Determine what the message length is
- Create a new array of message length
- read the new array.
Here's some sample code (the reader is a BufferedReader created elsewhere):
char[] chars = new char[4];
int charCount = reader.read(chars);
String messageLengthString = new String(chars);
int messageLength = Integer.parseInt(messageLengthString);
chars = new char[messageLength];
charCount = reader.read(chars);
if (charCount != messageLength)
{
// Something went wrong...
}
I know how to do this, but I need to worry about the character buffers not filling up? if so how should i handle it?
a source to share
Balls in Java are for text data. Are you sure the protocol actually determines the length of the message this way? Most likely the first four bytes represent 32-bit length.
If you are talking to C or C ++ developers, they can use "char" as a synonym for "byte".
EDIT: Ok, based on the comment:
I would create a method that accepts Reader
both the counter and calls it repeatedly read()
until it reads the right amount of data, or excludes. Something like that:
public static String readFully(Reader reader, int length) throws IOException
{
char[] buffer = new char[length];
int totalRead = 0;
while (totalRead < length)
{
int read = reader.read(buffer, totalRead, length-totalRead);
if (read == -1)
{
throw new IOException("Insufficient data");
}
totalRead += read;
}
return new String(buffer);
}
Then your code could be:
String lengthText = readFully(reader, 4);
int length = Integer.parseInt(lengthText);
String data = readFully(reader, length);
// Use data now
You should check what happens when they want to send less than 1000 (or more than 9999) characters, though ...
a source to share
Regarding the part of the question where you need to read a certain number of characters after you've established what it is, the following idiom is common with java.io.Readers:
int lengthToRead = getRequiredReadLength(); // Left as exercise to reader :-)
char[] content = new char[lengthToRead]
int from = 0;
while (lengthToRead > 0)
{
try
{
int nRead = reader.read(context, from, lengthToRead);
if (nRead == -1)
{
// End of stream reached before expected number of characters
// read so handle this appropriately - probably throw an exception
}
lengthToRead -= nRead;
from += nRead;
}
catch (IOException e)
{
// Handle exception
}
}
Since the call is read
guaranteed to return a non-zero result (call blocks until some data is available, the end of the stream is reached (returns -1), or an exception is thrown), this loop ensures that you read as many characters as you need while the stream can provide them.
In general, whenever multiple characters are asked from the reader at once, remember that there is no guarantee that many characters were actually supplied, and the return value must always be checked to see what happened. Otherwise, you will inevitably end up in some error cases, where parts of your stream will "disappear".
a source to share
Uh ... Isn't that char
16 bit in Java, for Unicode? I don't think you are doing the right thing by using characters to represent the bytes coming off the network. You should probably use ByteBuffer
something like ByteBuffer
.
If you know the maximum size of a single message, you don't have to just create one buffer, read four bytes into the buffer, parse them into int
or so on, and then do a new read using that size, overwriting the contents of the buffer.
UPDATE: The above assumes the protocol was binary and the usage char
was "C-ism". If the protocol is actually text, and the initial length of 4-char is a padded integer (in some base, I'm guessing 10?) Like "0047" or "6212" then some other approach is probably better not you need to go from bytes to characters.
a source to share