Исключение обработки нескольких сообщений внутри буфера [JAVA-Mina]

--- РЕДАКТИРОВАТЬ ниже

На самом деле я реализую Mina ProtocolCodecFilter для получения сообщений от последовательного устройства.

Кодек указывает несколько разных сообщений (с их pojos), и даже если реализация работает правильно в 99% случаев, у меня возникают проблемы с одним типом сообщения: единственное сообщение, которое не имеет фиксированной длины . Я могу знать минимальную длину, но не максимальную.

Это сообщение об исключении, которое я получаю (только важные части):

org.apache.mina.filter.codec.ProtocolDecoderException: org.apache.mina.core.buffer.BufferDataException: dataLength: -2143812863 (Hexdump: 02 01 A2 02 01 A0 02)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:25

...

Caused by: org.apache.mina.core.buffer.BufferDataException: dataLength: -2143812863
    at org.apache.mina.core.buffer.AbstractIoBuffer.prefixedDataAvailable(AbstractIoBuffer.java:2058)
    at my.codec.in.folder.codec.MAFrameDecoder.doDecode(MAFrameDecoder.java:29)
    at org.apache.mina.filter.codec.CumulativeProtocolDecoder.decode(CumulativeProtocolDecoder.java:178)
    at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:241)

Иногда dataLength отрицательный, иногда положительный (не нашел никакой подсказки о причине этого).

MAFrameDecoder:29 — это второе предложение моей реализации метода doDecode() CumulativeProtocolDecoder (MAX_SIZE=4096):

public boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) 
throws Exception 
    {
        boolean result=false;
        if(in.prefixedDataAvailable(4, MAX_SIZE)) //-->This is line 29
        {

         int length = in.getInt();
         byte[] idAndData = new byte[length];
         in.get(idAndData);


         //do things, read from buffer, create message, out.write, etc
         //if all has been correct, result=true

         }

     return result;

    }

При отладке ошибки с помощью TCP-сниффера мы выяснили, что исключение возникало при вставке нескольких сообщений в один и тот же IoBuffer (in).

Похоже, мой Decoder просто не может обрабатывать несколько сообщений внутри одного и того же буфера. Но, как я уже говорил, существует также проблема с сообщениями нефиксированной длины (о которой я действительно не могу знать, имеет ли она какое-то значение). В других реализациях doDecode я видел другие методы управления буфером, такие как:

while (in.hasRemaining())

or

InputStream is=in.asInputStream();

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

Надеюсь, вы можете мне помочь, любой совет будет очень признателен. :)

ps: кодировщик, который отправляет мне сообщения через буфер, имеет параметр autoExpand в false.



ИЗМЕНИТЬ 11.10.2014

Я изучал метод AbstractIoBuffer и обнаружил следующее:

@Override
public boolean prefixedDataAvailable(int prefixLength, int maxDataLength) {
    if (remaining() < prefixLength) {
        return false;
    }

    int dataLength;
    switch (prefixLength) {
    case 1:
        dataLength = getUnsigned(position());
        break;
    case 2:
        dataLength = getUnsignedShort(position());
        break;
    case 4:
        dataLength = getInt(position());
        break;
    default:
        throw new IllegalArgumentException("prefixLength: " + prefixLength);
    }

    if (dataLength < 0 || dataLength > maxDataLength) {
        throw new BufferDataException("dataLength: " + dataLength);
    }

    return remaining() - prefixLength >= dataLength;
}

PrefixLength, который я отправляю, равен 4, поэтому переключатель входит в последний допустимый случай:

dataLength = getInt(position());

После этого создается исключение BufferDataException с отрицательной длиной данных, что означает, что метод position() класса AbstractIoBuffer возвращает отрицательное значение.

Я всегда думал, что nioBuffer никогда не может содержать отрицательное значение параметра position. Любые подсказки, почему это происходит?


person aran    schedule 07.11.2014    source источник


Ответы (1)


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

Если байтов недостаточно, вы должны вернуть false, чтобы кумулятивный декодер протокола мог получить для вас больше данных.

Будьте осторожны, чтобы вернуть буфер в соответствующую позицию перед возвратом буфера, иначе вы потеряете данные о длине для следующей итерации. (Если вы используете 4 байта для длины, вы должны перемотать 4 байта).

Изменить: вы можете использовать методы mark() и reset() IoBuffer для достижения такого поведения.

person Asier    schedule 11.11.2014
comment
Большое спасибо за ваш краткий и действительно мощный ответ, вот и все! Иногда полученные данные являются неполными и не соответствуют длине, указанной в первых 4 байтах буфера. Просто нужно было отметить начальную позицию и сбросить буфер, когда in.remaining()‹length. - person aran; 11.11.2014