Right now, I'm trying to write a GUI based Java tic-tac-toe game that functions over a network connection. It essentially works at this point, however I have an intermittent error in which several chars sent over the network connection are lost during gameplay. One case looked like this, when println statements were added to message sends/reads:
Player 1:
Just sent ROW 14 COLUMN 11 GAMEOVER true
Player 2:
Just received ROW 14 COLUMN 11 GAMEOV
Im pretty sure the error is happening when I read over the network. The read takes place in its own thread, with a BufferedReader wrapped around the socket's InputStream, and looks like this:
try {
int input;
while((input = dataIn.read()) != -1 ){
char msgChar = (char)input;
String message = msgChar + "";
while(dataIn.ready()){
msgChar = (char)dataIn.read();
message+= msgChar;
}
System.out.println("Just received " + message);
this.processMessage(message);
}
this.sock.close();
}
My sendMessage method is pretty simple, (just a write over a DataOutputStream wrapped around the socket's outputstream) so I don't think the problem is happening there:
try {
dataOut.writeBytes(message);
System.out.println("Just sent " + message);
}
Any thoughts would be highly appreciated. Thanks!
As it turns out, the ready() method guaruntees only that the next read WON'T block. Consequently, !ready() does not guaruntee that the next read WILL block. Just that it could.
I believe that the problem here had to do with the TCP stack itself. Being stream-oriented, when bytes were written to the socket, TCP makes no guarantees as to the order or grouping of the bytes it sends. I suspect that the TCP stack was breaking up the sent string in a way that made sense to it, and that in the process, the ready() method must detect some sort of underlying break in the stream, and return false, in spite of the fact that more information is available.
I refactored the code to add a newline character to every message send, then simply performed a readLine() instead. This allowed my network protocol to be dependent on the newline character as a message delimiter, rather than the ready() method. I'm happy to say this fixed the problem.
Thanks for all your input!
Try flushing the OutputStream on the sender side. The last bytes might remain in some intenal buffers.
It is really important what types of streamed objects you use to operate with data. It seems to me that this troubleshooting is created by the fact that you use DataOutputStream for sending info, but something else for receiving. Try to send and receive info by DataOutputStream and DataInputStream respectively.
Matter fact, if you send something by calling dataOut.writeBoolean(b)
but trying to receive this thing by calling dataIn.readString(), you will eventually get nothing. DataInputStream and DataOutputStream are type-sensitive. Try to refactor your code keeping it in mind.
Moreover, some input streams return on invocation of read() a single byte. Here you try to convert this one single byte into char, while in java char by default consists of two bytes.
msgChar = (char)dataIn.read();
Check whether it is a reason of data loss.
Related
I have a small TCP server program and a corresponding client, and they communicate via ServerSocket and Socket classes and DataInputStream/DataOutputStream. And I have a problem with sending Strings to the server.
connection = new Socket("localhost", 2233);
outStream = new DataOutputStream(connection.getOutputStream());
outStream.writeBytes(fileName);
fileName is, at this point in time, a hard-coded String with the value "listener.jardesc". The server reads the string with the following code:
inStream = new DataInputStream(connection.getInputStream());
String fileName = inStream.readLine();
The string is received properly, but three zero-value bytes have been added to the end. Why is that and how can I stop it from happening? (I could, of course, trim the received string or somehow else stop this problem from mattering, but I'd rather prevent the problem completely)
I'm just going to throw this out there. You're using the readLine() method which has been deprecated in Java 5, 6 & 7. The API docs state quite clearly that this method "does not properly convert bytes to characters". I would read it as bytes or use a Buffered Reader.
http://docs.oracle.com/javase/1.5.0/docs/api/java/io/DataInputStream.html#readLine%28%29
writeBytes() does not add extra bytes.
The code you've written is invalid, as you aren't writing a newline. Therefore it doesn't work, and blocks forever in readLine().
In trying to debug this you appear to have read the bytes some other way, probably with read(); and to have ignored the return value returned by read, and to have concluded that read() filled the buffer you provided, when it didn't, leaving three bytes in their initial state, which is zero.
We have a Java code talking to external system over TCP connections with xml messages encoded in UTF-8.
The message received begin with '?'. SO the XML received is
?<begin>message</begin>
There is a real doubt if the first character is indeed '?'. At the moment, we cannot ask the external system if/what.
The code snippet for reading the stream is as below.
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
int readByte = reader.read();
if (readByte <= 0) {
inputStream.close();
}
builder.append((char) readByte);
We are currently trying to log the raw bytes int readByte = inputStream.read(). The logs will take few days to be received.
In the mean time, I was wondering how we could ascertain at our end if it was truly a '?' and not a decoding issue?
I suspect strongly you have a byte-order-mark at the beginning of your doc. That won't render as a valid character, and consequently could appear as a question mark. Can you dump the raw bytes out and check for that sequence ?
Your question seems to boil down to this:
Can we ascertain the real value of the first byte of the message without actually looking at it.
The answer is "No, you can't". (Obviously!)
...
However, if you could intercept the TCP/IP traffic from the external system with a packet sniffer (aka traffic monitoring tool), then dumping the first byte or bytes of the message would be simple ... requiring no code changes.
Is logging the int returned by inputStream.read() the correct way to to analyse the bytes received. Or does the word length of the OS or other environment variables come into picture.
The InputStream.read() method returns either a single (unsigned) byte of data (in the range 0 to 255 inclusive) or -1 to indicate "end of stream". It is not sensitive to the "word length" or anything else.
In short, provided you treat the results appropriately, calling read() should give you the data you need to see what the bytes in the stream really are.
My goal is to send different kind of messages from client to server, and it will be text based. The thing I am uncertain of is how to del with partial reads here. I will have to be sure that I get a whole message and nothing more.
Do anyone have experience with that?
Here is what I have so far:
private void handleNewClientMessage(SelectionKey key) throws IOException {
SocketChannel sendingChannel = (SocketChannel) key.channel();
ByteBuffer receivingBuffer = ByteBuffer.allocate(2048);
int bytesRead = sendingChannel.read(receivingBuffer);
if (bytesRead > 0) {
receivingBuffer.flip();
byte[] array = new byte[receivingBuffer.limit()];
receivingBuffer.get(array);
String message = new String(array);
System.out.println("Server received " +message);
}
selector.wakeup();
}
But I have no way of "ending" the message and be certain to have one full message.
Best regards,
O
You can never be sure you won't read more than one message unless you only read one byte at a time. (Which I don't suggest).
Instead I would read as much as you can into a ByteBuffer and then parse it to find the end of the message e.g. a newline for text.
When you find the end of a line extract it and convert it to a String and process it. repeat until you have a partial message (or nothing left)
If you find you have only part of a message, you compact() (if position() > 0) when you have and try to read() some more.
This will allows you to read as many messages at once as you can but can also handle incomplete messages.
Note: You will need to keep the ByteBuffer for a connection so you know what partial messages you have read before.
Note: this is will not work if you have a message which is larger than your buffer size. I suggest using a recycled direct ByteBuffer of say 1+ MB. With direct ByteBuffers only the pages of the ByteBuffer which are used get allocated to real memory.
If you are concerned about performance I would re-use your byte[] where possible. You only need to re-allocate it if you need more space than you have already.
BTW, You might find using a BufferedReader with Plain IO is much simpler to use, but still performance well enough.
I have some code that tries to read in a Google Protocol Buffer message from a socket in Java. However, the mergeDelimitedFrom() method can throw an IOException if it reads in invalid data or if the socket connection is reset (and probably other reasons). If the connection is reset I would like to exit out of the loop, but if it is just a invalid message I would like to continue running. One thought is to just have some sort of exception counter and exit after X consecutive failures, but I was hoping to be able to figure out what type of error occurs instead of being in the dark.
This is basically the code I have:
while (m_Running)
{
SomeMessage message = null;
try
{
final Builder builder = SomeMessage.newBuilder();
if (builder.mergeDelimitedFrom(m_InputStream))
{
message = builder.build();
}
else
{
// Google protocol buffers doesn't document it very well
// but if mergeDelimietedFrom returns false then it has
// reached the end of the input stream. For a socket, no
// more data will be coming so exit from the thread
m_Running = false;
}
}
catch (final IOException e)
{
// what should really be done here ???
}
}
Just don't do it. If you are reading protocol buffer objects directly off a socket, then you are effectively defining your own application protocol. It's harder than you might think to do it right - there is a good general description of some of the problems at On the Design of Application Protocols. It's important to understand framing - determining where one message ends and another begins.
Which leads us to some advice from the inventors of protobuf at https://developers.google.com/protocol-buffers/docs/techniques. The key piece of advice is this:
If you want to write multiple messages to a single file or stream, it is up to you to keep track of where one message ends and the next begins.
I recommend that you decide on a framing protocol to divide the stream into messages, then write some custom socket code to handle the work or reading bytes of the sockets, dividing them into byte arrays where each byte array is known to contain exactly one message, then finally use protobuf to deserialize each message-byte-array into an object. Guaranteed no IOException protobuf deserialization.
You'll still have to deal with IOExceptions but it will be at a lower level where you are just reading byte arrays and you'll know exactly how much data has been deserialized when the error occurs.
Also consider using something like netty to help with the socket code.
I have a client and a server both running in C. My task is to introduce java program in which I create a server to the C client and a client to the C server. I am successful in trying to get the connections set up properly. However the problem is in communicating the data between both C programs. Below is what I have written in my java program:
while(true){
while((userInput1=br1.readLine())!=null||(userInput2=br2.readLine())!=null){
if(userInput1=!null){
bw1.write(userInput1);
bw1.flush();
}
if(userInput2=!null){
bw2.write(userInput2);
bw2.flush();
}
}
While debugging the above, it is seen that the execution is stuck at the second while statement meaning that the input stream is waiting for the input for the C client for ever. I am using BufferedReader and BufferedWriter for the streams. The C client and server are using send and recv functions to communicate.
Kindly help with any inputs to make the java program help both the C programs communicate with each other as they do without this.
Have you correctly considered the effect of Java's "short circuit" or operator?
With || if the first clause is true the second is never evaluated.
while(
(userInput1=br1.readLine())!=null ||
(userInput2=br2.readLine())!=null) {
So you successfully read
userInput1=br1.readLine())!=null
and immediately enter your processing, then come back to while and read the next line into userInput1 again. Hence userInput2 never will receive a value.
You need separate logic like
read first line
read second line
But exactly what should you do when reading line2 and a the data is not ready? Try again? Is the line you read next the expected line2 or a new line1? This is quite tricky to get right.
I would prefer not to rely on two separate readlines in my protocol.
while((userInput1=br1.readLine())!=null||(userInput2=br2.readLine())!=null){
This condition means that you are going to read br1 all the way to EOS before you ever read anything from br2. Is that what you really intended?
Conversely, if you are stuck at br2.readLine() it means two things: (a) br1 is at EOS, and (b) the peer associated with br2 hasn't sent anything, or at least hasn't sent a line terminated by a newline.
Are you perhaps suffering from the common delusion that readLine() returns null when there is no data ready to be read?
Also you are reading lines terminated by newlines, which are removed by the readLine() call, and then writing them out without any newlines, which can hardly be correct.
It appears to me that what you are really writing is a proxy, in which case you need two threads per socket, one reading from A and writing to B, and the other reading from B and writing to A. And if it's a proxy you should use InputStreams and OutputStreams rather than Readers and Writers, as you probably have no reason to inspect the data, and you therefore shouldn't put it through the byte->char and char->byte conversion processes implied by using Readers and Writers. There are further subtleties when writing proxies but I'll wait for your confirmation before elucidating them.
the reason I am using the parity character is to interpret the end of the stream. Otherwise using using just the read() is making the program halt for the input forever (even after the actual had sent all its data). Am using the ready() in the following way:
//The proxy client
while(true){
if(br1.ready()){
while((temp1=br1.read())!=(int)par)
userInput1=userInput1+(char)temp1;
System.out.println("Input to Actual Server: " + userInput1);
bw1.write(userInput1);
bw1.flush();
System.out.flush();
userInput1="";
temp1=0;
}
if(br2.ready()){
while((temp2=br2.read())!=(int)par)
userInput2=userInput2+(char)temp2;
System.out.println("Response from Actual Server: " + userInput2);
userInput2=userInput2+par;
bw2.write(userInput2);
bw2.flush();
System.out.flush();
userInput2="";
temp2=0;
}
}
//The proxy server
while(true){
if(br1.ready()){
while((temp1=br1.read())!=(int)par)
userInput1=userInput1+(char)temp1;
System.out.println("Input from Actual Client: " + userInput1);
userInput1=userInput1+par;
bw1.write(userInput1);
bw1.flush();
System.out.flush();
userInput1="";
temp1=0;
}
if(br2.ready()){
while((temp2=br2.read())!=(int)par)
userInput2=userInput2+(char)temp2;
System.out.println("Response to Actual Client: " + userInput2);
bw2.write(userInput2);
bw2.flush();
System.out.flush();
userInput2="";
temp2=0;
}
}
Kindly suggest if there is any problem of using ready().