Java SocketChannel есть мои байты - PullRequest
1 голос
/ 16 ноября 2009

Я создал SocketChannel на удаленном сервере для отправки и получения сообщений на Tomcat. Чтобы получать сообщения с удаленного компьютера, я использовал поток, посвященный задаче (только этот поток будет читать из сокета, больше ничего).

Когда в SocketChannel поступает несколько байтов (я продолжаю опрашивать SocketChannel о неблокирующем режиме для новых данных), я сначала читаю 4 байта, чтобы получить длину следующего сообщения, затем выделяю и читаю х байтов из SocketChannel , который затем декодируется и реконструируется в сообщение.

Ниже мой код для получающей нити:

@Override
public void run() {

    while (true) { //Don't exit thread

        //Attempt to read the size of the incoming message
        ByteBuffer buf = ByteBuffer.allocate(4);

        int bytesread = 0;
        try {
            while (buf.remaining() > 0) {
                bytesread = schannel.read(buf);

                if (bytesread == -1) { //Socket was terminated

                } 

                if (quitthread) break;
            }

        } catch (IOException ex) {

        }

        if (buf.remaining() == 0) {
            //Read the header
            byte[] header = buf.array();
            int msgsize = (0xFF & (int)header[0]) + ((0xFF & (int)header[1]) << 8)
                    + ((0xFF & (int)header[2]) << 16) + ((0xFF & (int)header[3]) << 24);

            //Read the message coming from the pipeline
            buf = ByteBuffer.allocate(msgsize);
            try {
                while (buf.remaining() > 0) {
                    bytesread = schannel.read(buf);

                    if (bytesread == -1) { //Socket was terminated

                    }

                    if (quitthread) break;
                }
            } catch (IOException ex) {

            }

            parent.recvMessage(buf.array());
        }

        if (quitthread) {
            break;
        }
    }

}

Первые байты, которые я получил от SocketChannel, в порядке, и я успешно расшифровал сообщение. Однако в следующий раз, когда я прочитал из SocketChannel, сокет пропустил вперед около 100 байтов, что привело к тому, что неправильные байты были прочитаны и интерпретированы как длина, что привело к повреждению всего.

Что не так с кодом? Никакой другой поток не читает из SocketChannel.

Ответы [ 2 ]

1 голос
/ 16 ноября 2009

Ваши круглые скобки выключены, код:

(0xFF & ((int)header[1] << 8))

, который всегда равен 0 (то же самое с << 16 и << 24), я думаю, вы имели в виду: </p>

((0xFF & ((int)header[1])) << 8)

Это приведет к тому, что будет прочитано недостаточно байтов сообщения, что также приведет к несоответствию синхронизации (в отличие от чтения слишком большого числа).

Редактировать : теперь вы исправили вышесказанное, я не вижу ничего плохого. Не могли бы вы сообщить нам соотношение между длиной первого сообщения и точным количеством байтов, которые были съедены?

Исходя из показанного кода, мое единственное предположение состоит в том, что вы отредактировали некоторые из показанных примеров поведения, которые могут повлиять на канал, на этот канал ссылаются в другом месте?

Если строка:

ByteBuffer buf = ByteBuffer.allocate(4);

будет за пределами while, что приведет к описанному вами поведению, но в вашем примере кода это не так.

0 голосов
/ 17 ноября 2009

Полагаю, когда вы говорите, что опрашиваете сокет в неблокирующем режиме, вы имеете в виду, что используете «стандартный» подход Selector.select()?

Когда select возвращает и указывает, что есть данные, доступные для чтения из сокета, вы должны прочитать только те байты, которые доступны, перед повторным вводом вызова select (). Если read () возвращает -1, это означает, что больше байтов больше нет для немедленного чтения в буфере. Это не означает, что сокет был закрыт. Поэтому я подозреваю, что вы пытаетесь полностью заполнить буфер перед возвратом, это неправильно. Даже если он работает, ваш поток ввода-вывода будет постоянно вращаться, пока поступают данные. В частности, похоже, что вы просто игнорируете возвращаемое значение -1. ​​

Подумайте о перестройке вашего кода, чтобы использовать подход с конечным автоматом. Например, я реализовал это в прошлом, используя модель с тремя состояниями: IDLE, READ_MESSAGE_LENGTH и READ_MESSAGE.

...