Java DataOutputStream не записывает все сообщения (сокет TCP)

в моем клиент-серверном приложении я обнаружил странную ошибку. Я получил следующие методы:

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

Каждый метод отправляет массив байтов на клиентскую сторону.

Если я вызываю только 2 из них, все работает нормально, и клиентская сторона получает все отправленные массивы байтов. Но если я вызову все 3 из них, только первый и второй прибудут к клиенту, порядок следующих методов не имеет значения. но Сервер говорит, что все они были отправлены. Чтобы написать клиенту iam, используя метод write(byte[] b, int off, int len);, все длины внутри пакетов также имеют смысл. А вот и странный момент:

если я добавлю Thread.sleep(1000); после второго метода, третий теперь прибывает к клиенту после сна. Я также пытался сбросить DataOutputStream после каждого вызова записи, но это не помогло.

РЕДАКТИРОВАТЬ:

Итак, скажем, я бы отправил 3 входа-ответа

Метод, который дает мне байт []:

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;
}

И метод отправки:

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);
    }
}

Итак, если я вызову sendLoginResponse(); три раза подряд, клиент получит только 2 байтовых массива, но сервер скажет, что он был отправлен 3 раза. Если я добавлю

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

Клиент, который читает сообщение, работает в потоке:

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);
        }
    }
}

спасибо!


person Moritz Schmidt    schedule 13.10.2016    source источник
comment
Покажите какой-нибудь код, пожалуйста.   -  person Prabhu    schedule 13.10.2016
comment
Пожалуйста, предоставьте свои источники. Как мы можем анализировать ваш код, имея только имена методов?   -  person eg04lt3r    schedule 13.10.2016
comment
Я отредактировал, надеюсь, вы прочтете! Благодарю вас!   -  person Moritz Schmidt    schedule 13.10.2016
comment
Где код, который читает это сообщение? И почему ты держишь собаку и сам лаешь? У вас есть DataOutputStream, который уже может записывать любые примитивные данные, но вы сами создаете свои сообщения?   -  person user207421    schedule 13.10.2016
comment
Я должен создавать сообщения, потому что все они следуют моему RFC, и это единственный способ сделать их легко читаемыми. Я добавил Клиент. Все пакеты начинаются с 1 байтового типа длиной 2 байта и полезной нагрузкой в ​​зависимости от того, какой тип имеет пакет.   -  person Moritz Schmidt    schedule 13.10.2016
comment
Вы не показали достаточно кода получения. Что происходит после этого? Какая польза от byteCount? И нет, это не «единственный способ облегчить чтение». Вам вообще не нужен метод getLoginResponse(). Я бы переименовал его в sendLoginRespinse() и предоставил ему DataOutputStream для записи. И наоборот, код чтения должен использовать readByte(), readShort(), а затем readFully(). Не нужно изобретать велосипед.   -  person user207421    schedule 13.10.2016


Ответы (2)


если я вызову sendLoginResponse(); три раза подряд клиент получает только 2 байтовых массива, но сервер говорит, что он был отправлен 3 раза.

Это связано с тем, что TCP является потоково-ориентированным протоколом. Это означает, что он не знает и не заботится о том, как разграничиваются ваши сообщения. В TCP нет концепции отдельных сообщений, только поток байтов с гарантией сохранения порядка байтов.

Таким образом, когда отправитель вызывает три write, массивы из трех байтов просто объединяются по соединению и достигают получателя в том же порядке, но получателю не обязательно нужно три read, чтобы получить все байты, и даже если это займет три read, read не обязательно дает вам один и тот же массив байтов, переданный каждому соответствующему write.

В вашем сообщении уже есть необходимая информация для получения отдельного сообщения из потока байтов:

// 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);
person xiaofeng.li    schedule 13.10.2016
comment
Большое спасибо! Правильно ли я понимаю, скажем, сервер отправляет 10 байтов клиенту, но клиент читает только 4 байта, остальные 6 байтов все еще находятся в буфере и могут быть красными, когда я захочу? И если я отправлю другое сообщение, я все равно смогу прочитать оставшиеся 6 байтов без каких-либо проблем? Еще раз большое спасибо! - person Moritz Schmidt; 14.10.2016
comment
Хорошо, теперь мне придется исправить некоторые строки кода и надеяться на лучшее. Я дам вам знать завтра, если все пойдет хорошо, но я уверен, что так и будет. - person Moritz Schmidt; 14.10.2016
comment
Да, в принципе так и будет. TCP предлагает надежную и упорядоченную передачу потока байтов. - person xiaofeng.li; 14.10.2016

DataOutputStream и TCP не теряют данные.

Как почти всегда видно в вопросах такого рода, проблема находится на принимающей стороне. Вероятно, вы предполагаете, что `read()' заполняет буфер, и игнорируете количество, которое она возвращает.

Основываясь на описании вашего протокола в комментариях, вы должны использовать DataInputStream.readFully() в этом случае:

byte type = din,readByte();
int length = din.readShort();
byte[] data = new byte[length];
din.readFully(data);
person user207421    schedule 13.10.2016
comment
readShort не будет работать, потому что порядок следования байтов не совпадает. - person xiaofeng.li; 14.10.2016
comment
@LukeLee Так поменяй местами. Легче, чем то, что он делает сейчас. Какой RFC-протокол он использует, если он использует обратный порядок байтов в сети? Я не думал, что они есть. - person user207421; 14.10.2016