We are integrating with an external product that requires us to communicate with it using Java sockets. We've been able to read small responses from the server with no issues, but larger responses are causing some headaches.
I made some changes to the socket handling logic and now we seem to be able to read large responses 90% of the time. It does still fail occasionally. Failure, in this case, means that the Java client stops reading from the socket before the entire response has been read. The client thinks that the read operation is finished, and stops normally - there are no exceptions or timeouts involved.
Here's what the current logic looks like:
StringWriter response = new StringWriter();
PrintWriter writer = new PrintWriter(response);
char[] buf = new char[4096];
int readChars;
do {
readChars = responseBufferedReader.read(buf);
writer.write(buf, 0, readChars);
} while(readChars != -1 && responseBufferedReader.ready());
responseBufferedReader is a BufferedReader wrapped around an InputStreamReader wrapped around the Socket's InputStream.
This code works most of the time, but it seems like checking for readChars != -1 and ready() are not reliable enough to indicate if we've read all of the content from the server. Comparing the number of read characters to the buffer size is also not reliable, since the server seems to be a little slow at sending the response back causing these numbers to differ.
I've tried changing the size of the character buffer; it helped, but it's still not working 100% of the time.
Is there a better and more reliable way to read entirely from a Socket without knowing the size of the expected response? I've been doing some research on SocketChannels, but I'm not sure if there's any benefit to be had by switching.
In case it helps, we're making a single, blocking Socket connection to the server. The Socket is configured for a 100 second timeout
You shouldn't be checking whether the BufferedReader is ready() to tell if you're done. It's possible that bytes are still being read off of the wire and the BufferedReader has nothing for you, but the Socket is not yet closed.
I'm not sure what value the BufferedReader is getting you (well, it might help your performance). I would expect the following to work better:
StringWriter response = new StringWriter();
PrintWriter writer = new PrintWriter(response);
char[] buf = new char[4096];
int readChars;
do {
readChars = inputStreamReader.read(buf);
writer.write(buf, 0, readChars);
} while(readChars != -1);
I think that you're probably dropping out of the loop when the network is the bottleneck - the loop processes the results fast enough so that the Reader isn't ready yet and you assume that you're done reading (even though you're not).
See the answers to the question at Java blocking socket returning incomplete ByteBuffer
If the network breaks the message into packets, you will drop out of your loop before reading all packets.
See my answer to this question. The same answer applies whenever you are reading from a stream, and the only reliable way to know when you're done if EOF doesn't apply (as in this case) is to encode the end-of-message in the protocol somehow -- how you do it is up to you.
Edit:
If you can't modify the protocol to include message boundaries, you might investigate a timeout-based approach. You have to decide how long to wait before detecting the end of a message... but then you have to handle the case where you get two messages in the same stream. Can your code handle this situation now? If so, then your answer is to buffer the input and use that logic to detect record boundaries.
Related
I am reading a file being uploaded over the web and it is being sent through http and we are receiving through a BufferedInputStream. Sometimes we get a timeout exception in the middle of reading from the stream. My working theory is that the connection is being closed from the client before we can process the whole file. The file is in the order of mb.
Does this theory make sense? Does the client need to keep the connection open in order for the server to completely read bytes from the input stream?
No good news in that case, data lost will occur.
Does this theory make sense? Does the client need to keep the connection open in order for the server to completely read bytes from the input stream?
No.
As long as the BufferedInputStream has bytes in the buffer, any calls to read() / read(byte[]) / read(byte[], int, int) will simply give you data from the buffer and will never even touch the underlying inputstream.
As long as you don't touch said inputstream, it cannot just start throwing exceptions out of the clear blue sky. You need to call something on the actual socket inputstream (be it read, close, flush, write - something), in order to get an exception thrown.
What could happen is a mixed mode operation: You call e.g:
var raw = socket.getInputStream();
var buffered = new BufferedInputStream(raw);
byte[] b = new byte[1000];
buffered.read(b); // actually reads 4000 bytes into buffer, giving 1000 of them.
// 3000 left in the buffer!
byte[] c = new byte[2000];
buffered.read(c); // works fine and never touches raw. Can't throw.
byte[] d = new byte[2000];
buffered.read(d); // 'mixed mode'
Here, in the 'mixed mode' situation, the first 1000 bytes are filled by the buffer, but then raw.available() is invoked (source: The actual source code of BufferedInputStream.java); if it returns a non-zero number, then more data is fetched from raw directly; if it is 0, read() just returns (read() is under no obligation to read ALL the requested bytes; it merely needs to [A] read at least 1, and [B] return how much it did read; usually you want readNBytes instead).
However, in.available() is allowed to throw. If it does, voila.
However, a normal TCP close would not cause TimeoutExceptions.
A much more likely scenario is the following: Your code is simply not processing the data fast enough. The sending entity at some point is just fed up with it all and refuses to continue to let you hog a file handle and just hangs up on you. If you're already using a buffer, perhaps there's some network equipment in between that is dog slow, or the server is configured with unrealistic expectations of how fast your network connections are.
I have a piece of code which reads the POST data from the Servlet Request's input stream. I am using java nio for reading the data.
For most cases and regular data, the code works perfectly fine. However in some cases where data is large (content length = 600000), the Channel's read method seems to fail with a Socket timeout error. Also this seems to happen only with IE 9, it is working fine with Firefox and Chrome.
While investigating this i figured, that while using IE, the post data seems to take a bit longer than the other browsers to be available for reading. So i put a Thread.sleep(400) before the code and the code started to work fine for IE as well.
I don't want to put a sleep before this code, one its just a workaround and not a proper solution, second, there is no correct sleep time, since if the data increases, 400 might not be enough.
Is there a way where i can tell the channel to not time out or remove the timeout altogether?
Below is code being used,
ReadableByteChannel channel = Channels.newChannel(inputStream);
byte[] postData = new byte[contentLength];
ByteBuffer buf = ByteBuffer.allocateDirect(contentLength);
int numRead = 0;
int counter = 0;
while (numRead >= 0) {
buf.rewind();
numRead = channel.read(buf);
buf.rewind();
for (int i = 0; i < numRead; i++) {
postData[counter++] = buf.get();
}
}
return postData;
The inputStream is directly via request.getInputStream() and content length is via request.getContentLength().
The container used is Tomcat 7.0.42 in embedded mode.
If you can read the content length header, you can increase the sleep time based on it. That should at least make your code function in different scenarios. It's a workaround of course, but it seems like a solid one. Alternatively, you can set the socket timeout wait to a higher number. This change will be for all your servlets however, and is much less flexible.
Since i couldn't find any appropriate solution for this problem. I have continued with my existing work around i.e. sleep for some time. But the issue i had was how long do i sleep, since larger the data i would need to sleep more. And there is no correct way of knowing that, since i couldn't derive any formula out of the content length, the sleep times were random.
So the next best thing i could apply was, i caught the Socket Timeout Exception and sleep for the least minimum time that i knew was needed i.e. 400ms. And then tried again, set the logic for 3 retries before giving up. So far that has worked, for increasing amount of data.
Your read timeout is too short. Increase it.
But I would throw this NIO code away and just read directly from the input stream. With DataInputStream.readFully() it's one line of code. Your NIO code does that anyway but in a very roundabout way with several extra layers.
I've implemented a C server and a Java client which communicate to each other through TCP. The problem is when the client sends a string, the recv() call in C server just reads a character and returns immediately. If I put a breakpoint at recv() and then do a step-over, the server receives the whole string that was sent.
Here's the recv call I'm using in C
char tempBuffer[256] = {'\0'};
int retVal = recv(hClientSocket, tempBuffer, sizeof(tempBuffer), 0);
And here's the Java client.
clientSocket = new Socket("127.0.0.1", 24886);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
outToServer.writeBytes("alert(\"hi\")");
My question is, is there a way in which I can make this work without having to put a while loop in my C server code to receive each character separately? What's the best way to do this?
I implemented the C++ client given here and it works fine. So I'm guessing it's some problem in Java+C++ interoperability. Thanks!
Although what Scott M says is true on a Java side ( about using flush ), you will have to rework your whole communication protocol.
You will have to either tell to C-code, how many bytes are expected to be sent (this will be the easiest to track), or use some sort of termination character, to recognize that the string input is over ( e.g. null terminator byte ).
There is absolutely no guarantee that the network layer will not chop the transition arbitrarily, so code defensively and expect recv always to recieve a partial data, which you will have to assemble for your internal consumption.
Try flushing the java stream. They are normally buffered, which will cause some delay in the writing of bytes to a stream. Long enough to mess up code at full speed, short enough to send the whole string while debugging.
With TCP there's no way around "putting a while loop around recv()". TCP is a stream protocol built on top of limited-size packet service (IP). TCP does its own "packetization", and is actually very smart about it. This implies that you never know how many bytes the next read from the socket will return.
The well-known strategy that works is to design your application-level protocol so the message itself tells how big it is, then read in a loop until you have full message to process.
Having said that, there's a kludge, namely MSG_WAITALL flag for the recv(2) call, that you can use to wait for given amount of data, assuming you know how much to expect.
I once tried by having a header indicating length of string .. In that way din have to check for the end of message..
I'm using a UNIX socket to facilitate communication on and Android device between a system level daemon I've got running in C and an application I've got running in Java. I'm much more of a C coder than a Java coder, so I'm having some issues when trying to read in data from the socket on the Java side. Currently, my code is as follows:
try{//Prepare to write the command and read the ACK
InputStream is = receiver.getInputStream();
OutputStream os = receiver.getOutputStream();
BufferedReader in = new BufferedReader(new InputStreamReader(is));
os.write(message.getBytes());
//FIX THIS!! The following assumes that there is a newline-
// terminated chunk of data waiting for us in the buffer. If
// that assumption is wrong, the code will hang. Find some way
// to determine if/how much data is waiting for us in the socket!
String str = in.readLine();
is.close();
os.close();
receiver.close();
return str;
}catch(IOException ex){
Log.e(TAG,(ex.toString()+"\n"));
return null;
}
As my comment indicates, this implementation works(ish), but has the very bad design flaw of requiring that the server side socket code responds with a newline terminated string. If that's not the case, the world ends.
What I'm trying to figure out is if there is a better means of accomplishing this task. Is it possible to determine if there is any data waiting to be read in a buffered reader, and if so, what the size of that data is? If not, is there a better way of reading in data over a UNIX socket on the Java side of Android that I'm not familiar with?
Thanks in advance!
if the protocol requires a newline, your impl is appropriate. the code SHOULD be blocked until a newline is read. what else can you do but wait?
of course, usually you don't want to wait forever, you need a timeout. see Socket.setSoTimeout()
if a newline is not necessarily given by server, and you just want to read any data as soon as available, use InputStream.read()
I have a client connecting to my server. The client sends some messages to the server which I do not care about and do not want to waste time parsing its messages if I'm not going to be using them. All the i/o I'm using is simple java i/o, not nio.
If I create the input stream and just never read from it, can that buffer fill up and cause problems? If so, is there something I can do or a property I can set to have it just throw away data that it sees?
Now what if the server doesn't create the input stream at all? Will that cause any problems on the client/sending side?
Please let me know.
Thanks,
jbu
When you accept a connection from a client, you get an InputStream. If you don't read from that stream, the client's data will buffer up. Eventually, the buffer will fill up and the client will block when it tries to write more data. If the client writes all of its data before reading a response from the server, you will end up with a pretty classic deadlock situation. If you really don't care about the data from the client, just read (or call skip) until EOF and drop the data. Alternatively, if it's not a standard request/response (like HTTP) protocol, fire up a new thread that continually reads the stream to keep it from getting backed up.
If you get no useful data from the client, what's the point of allowing it to connect?
I'm not sure of the implications of never reading from a buffer in Java -- I'd guess that eventually the OS would stop accepting data on that socket, but I'm not sure there.
Why don't you just call the skip method of your InputStream occasionally with a large number, to ensure that you discard the data?
InputStream in = ....
byte[] buffer = new byte[4096] // or whatever
while(true)
in.read(buffer);
if you accept the connection, you should read the data. to tell you the truth i have never seen (or could forsee) a situation where this (a server that ignores all data) could be useful.
I think you get the InputStream once you accept the request, so if you don't acknowledge that request the underlying framework (i.e. tomcat) will drop that request (after some lapsed time).
Regards.