0

in my Client-Server application I found a strange error. I got the following Methods :

sendLoginResponse();
sendPlayerList();
sendCurrentLevelState();

Each Methods sends a byte array to the Client-Side

If I only call 2 of them, everything works fine and the Client-Side gets all the sent byte arrays. But if I call all 3 of them only the first and second one arrive the Client, the order of the following methods doesnt matter. but the Server says all of them were send. To write to the Client iam using the write(byte[] b, int off, int len); method all The lenghts within the packages make sense too. Here comes the strange point:

if I add a Thread.sleep(1000); after the second Method, the third one does now arrive the Client after the sleep. I Have also tried to flush the DataOutputStream after every write call, but this didnt help.

EDIT:

So let's say I'd send 3 Login-Response

The Method's that gives me the byte[]:

public byte[] getLoginResponse(int playerID){
    byte[] msg = new byte[4];
    short shortMsgLength = 4;
    byte[] msgLength = shortToBytes(shortMsgLength);
    msg[0] = 2;
    msg[1] = msgLength[0];
    msg[2] = msgLength[1];
    msg[3] = (byte) playerID;
    return msg;
}

private byte[] shortToBytes(short value) {
    byte[] returnByteArray = new byte[2];
    returnByteArray[0] = (byte) (value & 0xff);
    returnByteArray[1] = (byte) ((value >>> 8) & 0xff);
    return returnByteArray;
}

And the Send Method:

private void sendLoginResponse() {
    try{
        byte[] msg = rfcObject.getLoginResponse(playerID);
        out.write(msg,0,msg.length);
    }catch(Exception e){
        System.err.println(e.getMessage());
        System.exit(0);
    }
}

So if I call the sendLoginResponse(); three times in a row, the client only recieves 2 byte-arrays, but the server says it has been sent 3 times. If i add a

Thread.sleep(1000); `after the second Method-Call, everything works fine..`

The Client that reads the message runs in a Thread:

public void run(){
    while(true){
        try {
            byte[] data = new byte[MAX_DATA_SIZE]; // MAX_DATA = 255
            byteCount = in.read(data);

        } catch (IOException ex) {
            handleExceptionError(ex);
        }
    }
}

thank you!

Moritz Schmidt
  • 103
  • 2
  • 6
  • Show some code, please. – Prabhu Oct 13 '16 at 18:15
  • Please, provide your sources. How we can analyze your code only having methods names? – eg04lt3r Oct 13 '16 at 18:16
  • I edited it, I hope you can read it! Thank you! – Moritz Schmidt Oct 13 '16 at 18:37
  • Where is the code that reads this message? And why are you keeping a dog and barking yourself? You have a DataOutputStream that can already write any primitive data, and yet you're building up your messages yourself? – user207421 Oct 13 '16 at 19:51
  • I have to build the messages because they all follow my on defined RFC and this is the only way to keep it easy to read. I added the Client. All the packages start with 1 Byte Type 2 Bytes length and the payload depending on which type the package has. – Moritz Schmidt Oct 13 '16 at 20:20
  • You haven't shown enough of the receiving code. What happens after that? What use is made of `byteCount`? And no, it isn't the 'only way to keep it easy to read'. You don't need the `getLoginResponse()` method at all. I would rename it to `sendLoginRespinse()` and provide it with the `DataOutputStream` to write to. Conversely the reading code should use `readByte()`, `readShort()`, and then `readFully()`. No need to reinvent the wheel. – user207421 Oct 13 '16 at 20:45

2 Answers2

0

DataOutputStream and TCP don't lose data.

As almost invariable seen in questions of this nature, the problem is at the receiving end. You are probably assuming that `read()' fills the buffer, and ignoring the count that it returns.

Based on your protocol description in comments, you should be using DataInputStream.readFully() in this circumstance:

byte type = din,readByte();
int length = din.readShort();
byte[] data = new byte[length];
din.readFully(data);
user207421
  • 305,947
  • 44
  • 307
  • 483
0

if I call the sendLoginResponse(); three times in a row, the client only recieves 2 byte-arrays, but the server says it has been sent 3 times.

This is because TCP is a stream-oriented protocol. Meaning it doesn't know or care how your messages are delimited. There's no concept of individual messages in TCP, just a stream of bytes, with the guarantee that the order of bytes is preserved.

So when the sender calls three write, the three byte arrays are simply concatenated over the connection and arrives at the receiver in the same order, but the receiver doesn't necessarily need three read to get all the bytes, and even if it does take three read, the read doesn't necessarily gives you the same byte array passed to each corresponding write.

Your message already have the necessary information to get the individual message back from the byte stream:

// Client code for reading individual messages from a TCP connection

byte type = din.readByte();

// Read the message length, little-endian.
// We cannot use din.readShort because it's big-endian
int lenLo = din.read();
int lenHi = din.read();
short len = (short)(lenLo | (lenHi << 8));
byte [] body = new byte[len];
din.readFully(body);
xiaofeng.li
  • 8,237
  • 2
  • 23
  • 30
  • Thank you so much! So do I understand this right, lets say the Server sends 10 Bytes to the Client, but the Client only reads 4 Bytes, the remaining 6 Bytes are still in the Buffer and can be red whenever I want? And if I send another message i could still read the 6 remaining Bytes without having any problems? Again thank you so much! – Moritz Schmidt Oct 14 '16 at 00:59
  • Okay I will have to fix some lines of code now and hope the best.. I let you know tomorrow if everything went well but im pretty sure it will – Moritz Schmidt Oct 14 '16 at 01:12
  • Yes, that's basically what will happen. TCP offers reliable and ordered transmission of a stream of bytes. – xiaofeng.li Oct 14 '16 at 01:14