Как анализировать / кодировать двоичные форматы сообщений? - PullRequest
7 голосов
/ 28 июля 2011

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

Например, мне приходится иметь дело с такими сообщениями:

+----------+---+---+----------+---------+--------------+
+uint32    +b   +b + uint32   +4bit enum+32 byte string+
+----------+---+---+----------+---------+--------------+

Где (b) - однобитовый флаг. Проблема заключается в том, что примитивные типы Java не совпадают с границами байтов, поэтому я не смог бы использовать DataOutputStream для кодирования этого, поскольку тип самого низкого уровня, который я могу записать, - это байт.

Существуют ли какие-либо библиотеки, стандартные или сторонние, для работы с произвольными форматами сообщений на битовом уровне?

Edit: Спасибо @Software Monkey за то, что он заставил меня взглянуть на мою спецификацию более внимательно. Спецификация, которую я использую, фактически выравнивает границы байтов, поэтому DataOutputStream подходит. Учитывая мой первоначальный вопрос, я бы пошел с решением, предложенным @emboss.

Edit: Хотя было обнаружено, что формат сообщения для этого вопроса находится в границах байтов, я натолкнулся на другой формат сообщения, применимый к исходному вопросу. Этот формат определяет 6-битное сопоставление символов, где каждый символ в действительности занимает всего 6 бит, а не полный байт, поэтому строки символов не выравниваются по границам байтов. Я обнаружил несколько двоичных потоков вывода, которые решают эту проблему. Как этот: http://introcs.cs.princeton.edu/java/stdlib/BinaryOut.java.html

Ответы [ 5 ]

5 голосов
/ 29 марта 2015

с Java Binary Block Parser скрипт для разбора сообщения будет

  class Parsed {
    @Bin int field1;
    @Bin (type = BinType.BIT) boolean field2;
    @Bin(type = BinType.BIT) boolean field3;
    @Bin int field4;
    @Bin(type = BinType.BIT) int enums;
    @Bin(type = BinType.UBYTE_ARRAY) String str;
  }

  Parsed parsed = JBBPParser.prepare("int field1; bit field2; bit field3; int field4; bit:4 enums; ubyte [32] str;").parse(STREAM).mapTo(Parsed.class);
5 голосов
/ 28 июля 2011

Существует встроенный тип byte в Java, и вы можете просто читать в byte[] буферы, используя InputStream # читать (byte []) и записывать в OutputStream, используя OutputStream #write (byte [], int, int) , поэтому в этом нет проблем.

Что касается ваших сообщений - как вы правильно заметили, самый маленький бит информации, который вы получаете за один раз, - это байт, поэтому вам придется сначала разбить формат сообщения на 8-битные куски:

Предположим, ваше сообщение находится в байте [] с именем data. Я также предполагаю, что порядок байтов меньше.

Длина uint32 - 32 бита -> это четыре байта. (Будьте осторожны при разборе этого в Java, целые и длинные значения Java подписаны, вам нужно это обработать. Простой способ избежать проблем - это использовать длинные значения. Data [0] заполняет биты 31 - 24, data [1] 23 - 16, биты данных [2] 15 - 8 и биты данных [3] - 7–0. Поэтому вам необходимо сместить их соответствующим образом влево и склеить их вместе с логическим ИЛИ:

long uint32 = ((data[0]&0xFF) << 24) | 
              ((data[1]&0xFF) << 16) | 
              ((data[2]&0xFF) << 8)  | 
               (data[3]&0xFF);

Далее, есть два отдельных бита. Я полагаю, вы должны проверить, включены ли они (1) или выключены (0). Для этого вы используете битовые маски и сравниваете свой байт с логическим AND.

Первый бит: (двоичная маска | 1 0 0 0 0 0 0 0 | = 128 = 0x80)

if ( (data[4] & 0x80 ) == 0x80 ) // on

Второй бит: (двоичная маска | 0 1 0 0 0 0 0 0 | = 64 = 0x40)

if ( (data[4] & 0x40 ) == 0x40 ) // on

Для составления следующего uint32 вам придется составлять байты за байтовые границы базовых данных. Например. для первого байта возьмите оставшиеся 6 бит данных [4], сдвиньте их на два влево (они будут битами 8–2 от uint32) и «добавьте» первые (старшие) два из данных [5], сдвинув их 6 бит справа (они займут оставшиеся 1 и 0 слот uint32). «Добавление» означает логически ИЛИ:

byte uint32Byte1 = (byte)( (data[4]&0xFF) << 2 | (data[5]&&0xFF) >> 6);

Построение вашего uint32 - это та же процедура, что и в первом примере. И так далее, и тому подобное.

4 голосов
/ 19 августа 2011

Просто чтобы добавить к ответу pholser, я думаю, что версия Preon будет выглядеть примерно так:

class DataStructure {
  @BoundNumber(size="32")  long       first; // uint32
  @Bound                   boolean    second; // boolean
  @Bound                   boolean    third; // boolean
  @BoundNumber(size="32")  long       fourth; // uint32
  @BoundNumber(size="4")   int        fifth; // enum
  @BoundString(size="32")  String     sixth; // string
}

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

Создание Codec для него и использование его для декодирования некоторых данных будет выглядеть примерно так:

Codec<DataStructure> codec = Codecs.create(DataStructure.class)
DataStructure data = Codecs.decode(codec, ....)
4 голосов
/ 28 июля 2011

Я слышал хорошие вещи о Преон .

2 голосов
/ 28 июля 2011

Вам необходимо применить битовую арифметику (операторы AND, OR, AND NOT), чтобы изменить или прочитать отдельные биты в байте в Java.Арифметические операторы &, |и ~

...