Серия Netty: встроенное обнаружение кадров

Java Netty

Это 17-й день моего участия в августовском испытании обновлений. Узнайте подробности события:Испытание августовского обновления

Введение

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

Frame detection

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

Netty предоставляет 4 кодировщика точек разделения, которые мы можем назвать обнаружением кадров: DelimiterBasedFrameDecoder, FixedLengthFrameDecoder, LengthFieldBasedFrameDecoder и LineBasedFrameDecoder.

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

DelimiterBasedFrameDecoder

Первый — DelimiterBasedFrameDecoder, по названию видно, что это декодер, который делит bytebuf по разделителю. Что такое разделитель?

В netty есть класс Delimiters, который определяет разделительные символы.В основном есть два разделителя, nulDelimiter и lineDelimiter:

public static ByteBuf[] nulDelimiter() {
        return new ByteBuf[] {
                Unpooled.wrappedBuffer(new byte[] { 0 }) };
    }

    public static ByteBuf[] lineDelimiter() {
        return new ByteBuf[] {
                Unpooled.wrappedBuffer(new byte[] { '\r', '\n' }),
                Unpooled.wrappedBuffer(new byte[] { '\n' }),
        };
    }

nullDelimiter используется для обработки 0x00, в основном используется для обработки сокета Flash XML или других подобных протоколов.

lineDelimiter используется для обработки возврата каретки и перевода строки, в основном при обработке текстовых файлов.

Для DelimiterBasedFrameDecoder, если есть несколько разделителей, он выберет тот, который разбивает ByteBuf наименьший.Например, если мы используем DelimiterBasedFrameDecoder(Delimiters.lineDelimiter()), поскольку на самом деле в lineDelimiter есть два метода разделения, нажмите Enter + перевод строки или перевод строки, если выполняются следующие условия:

   +--------------+
   | ABC\nDEF\r\n |
   +--------------+

DelimiterBasedFrameDecoder выберет кратчайший результат сегментации, то есть указанный выше контент будет разделен на:

   +-----+-----+
   | ABC | DEF |
   +-----+-----+

вместо

   +----------+
   | ABC\nDEF |
   +----------+

FixedLengthFrameDecoder

Этот класс делит ByteBuf на фиксированные длины, такие как следующие 4 байта полученной информации:

   +---+----+------+----+
   | A | BC | DEFG | HI |
   +---+----+------+----+

Если используется FixedLengthFrameDecoder(3), указанный выше ByteBuf будет разделен на следующие части:

   +-----+-----+-----+
   | ABC | DEF | GHI |
   +-----+-----+-----+

LengthFieldBasedFrameDecoder

Этот класс является более гибким и может извлекать последующий массив байтов в соответствии с полем длины в данных. LengthFieldBasedFrameDecoder очень гибкий, он имеет 4 свойства для управления ими: lengthFieldOffset, lengthFieldLength, lengthAdjustment и initialBytesToStrip.

lengthFieldOffset — начальная позиция поля длины, lengthFieldLength — длина самого поля длины, lengthAdjustment — настройка длины целевых данных, а initialBytesToStrip — количество байтов, которые необходимо удалить в процессе расшифровки. Не могу понять? Неважно, давайте возьмем несколько примеров.

Сначала рассмотрим самый простой:

   lengthFieldOffset   = 0
   lengthFieldLength   = 2
   lengthAdjustment    = 0
   initialBytesToStrip = 0 

   BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
   +--------+----------------+      +--------+----------------+
   | Length | Actual Content |----->| Length | Actual Content |
   | 0x000C | "HELLO, WORLD" |      | 0x000C | "HELLO, WORLD" |
   +--------+----------------+      +--------+----------------+

Приведенные выше настройки указывают, что длина начинается с бита 0 и составляет 2 байта. Где Ox00C=12, что также является длиной «HELLO, WORLD».

Если вам не нужно поле «Длина», вы можете удалить длину, установив initialBytesToStrip:

   lengthFieldOffset   = 0
   lengthFieldLength   = 2
   lengthAdjustment    = 0
   initialBytesToStrip = 2 (= length 字段的长度)
  
   BEFORE DECODE (14 bytes)         AFTER DECODE (12 bytes)
   +--------+----------------+      +----------------+
   | Length | Actual Content |----->| Actual Content |
   | 0x000C | "HELLO, WORLD" |      | "HELLO, WORLD" |
   +--------+----------------+      +----------------+

lengthAdjustment заключается в корректировке значения поля Length, так как в некоторых случаях поле Length может содержать длину всех данных, то есть Length+content, поэтому его нужно корректировать при парсинге, например, в следующем примере , фактическая длина на самом деле 0x0C, но входящее значение равно 0x0E, поэтому длину поля Length необходимо вычесть на 2, то есть lengthAdjustment устанавливается равным -2.

   lengthFieldOffset   =  0
   lengthFieldLength   =  2
   lengthAdjustment    = -2 (= Length字段的长度)
   initialBytesToStrip =  0

   BEFORE DECODE (14 bytes)         AFTER DECODE (14 bytes)
   +--------+----------------+      +--------+----------------+
   | Length | Actual Content |----->| Length | Actual Content |
   | 0x000E | "HELLO, WORLD" |      | 0x000E | "HELLO, WORLD" |
   +--------+----------------+      +--------+----------------+

LineBasedFrameDecoder

LineBasedFrameDecoder специально обрабатывает окончания строк в текстовых файлах. То есть "\n" и "\r\n", он очень похож на DelimiterBasedFrameDecoder, но DelimiterBasedFrameDecoder более общий.

Суммировать

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

Например, в StringDecoder, если использовался LineBasedFrameDecoder, то в методе декодирования можно предположить, что входящий ByteBuf представляет собой строку строк, тогда его можно использовать прямо так:

    protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        out.add(msg.toString(charset));
    }

Разве это не просто?

Эта статья была включена вwoohoo.floydpress.com/15-Нетти-нет…

Самая популярная интерпретация, самая глубокая галантерея, самые краткие уроки и множество трюков, о которых вы не знаете, ждут вас!

Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: «Программируйте эти вещи», разбирайтесь в технологиях, лучше поймите себя!