При использовании LINQ требуется помощь в разбивке байтового массива на данные, полученные из сокетов Silverlight - PullRequest
0 голосов
/ 18 мая 2010

Полученные пакеты сообщений содержат несколько сообщений, разделенных заголовком = 0xFD и нижним колонтитулом = 0xFE

// sample message packet with three
// different size messages
List<byte> receiveBuffer = new List<byte>();
receiveBuffer.AddRange(new byte[]
  { 0xFD, 1, 2, 0xFE, 
    0xFD, 1, 2, 3, 4, 5, 6, 7, 8, 0xFE,
    0xFD, 33, 65, 25, 44, 0xFE});


// note: this sample code is without synchronization, 
//       statements, error handling...etc.
while (receiveBuffer.Count > 0)
{
    var bytesInRange = receiveBuffer.TakeWhile(n => n != 0xFE);

    foreach (var n in bytesInRange)
        Console.WriteLine(n);

    // process message.. 
    // 1) remove bytes read from receive buffer
    // 2) construct message object...
    // 3) etc...

     receiveBuffer.RemoveRange(0, bytesInRange.Count());

}

Как видите, (включая заголовок / нижний колонтитул) первое сообщение в этом пакете сообщений содержит 4 байта, а второе сообщение содержит 10 байтов, а третье сообщение содержит 6 байтов.

В цикле while я ожидал, что TakeWhile добавит байты, которые не равны нижней части сообщения.

Примечание: поскольку я удаляю байты после их чтения, всегда можно ожидать, что заголовок будет в позиции «0».

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

Любая помощь будет принята с благодарностью. Большое спасибо!

Ответы [ 4 ]

3 голосов
/ 19 мая 2010

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

Обратите внимание на следующие советы по дизайну для классов "framer" с многолетним опытом:

  • Разделите вашу буферизацию сообщений на класс. Код буферизации достаточно сложен, не имея дело с асинхронными сокетами. Обработчики событий сокета могут быть ответственны за постоянное выполнение асинхронных чтений, обработку чтений нулевой длины и обработку ошибок. Затем они должны передать данные в буферный класс, который отвечает за фактическое кадрирование.
  • Когда вы пишете класс буферизации сообщений, вы получите более чистый код, если измените свой взгляд на данные. Вместо куска данных, поступающих в сокет и «проталкиваемых» через класс буферизации, воспринимайте его как класс буферизации, выдающий неявный «запрос чтения»; когда поступает кусок данных, выполняйте цикл, удовлетворяющий текущему «запросу на чтение», пока весь блок не будет израсходован.
0 голосов
/ 19 мая 2010

Поскольку у вас уже есть правильное кадрирование, есть ли причина, по которой решение Дэниела не работает для вас?

Если вы хотите что-то просто с помощью LINQ, это можно сделать:

int messageIndex = 0;
var test = receiveBuffer
    // Remove SOT bytes
    .Where(x => x != 0xFD)
    // Assign each byte as being part of a message, indexing on EOT
    .Select(x =>
        {
            if (x == 0xFE) ++messageIndex;
            return new { Byte = x, MessageIndex = (x == 0xFE ? -1 : messageIndex) };
        })
    // Remove EOT bytes
    .Where(x => x.MessageIndex != -1)
    // Group by message index
    .GroupBy(x => x.MessageIndex)
    // Strip message index and convert the bytes in each message to a List<byte>
    .Select(x => x.Select(y => y.Byte).ToList())
    // Execute the query, saving in a List<List<byte>>
    .ToList();

Однако я действительно чувствую, что решение Дэниела более читабельно и легко обслуживаемо. Остерегайтесь темной стороны.

Если вы настаиваете на LINQ, я бы порекомендовал написать метод расширения Partition, который очищает все это, чтобы вы могли иметь код, подобный receiveBuffer.Where(x => x != 0xFD).Partition(0xFE). В настоящее время такой функции не существует.

0 голосов
/ 19 мая 2010

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

receiveBuffer.RemoveRange(0, bytesInRange.Count() + 1);

вместо

receiveBuffer.RemoveRange(0, bytesInRange.Count());

С этим одним изменением код распечатывается каждый байт, кроме маркера конца каждого из трех сообщений.

Следующая вариация вашего кода печатает каждый байт тела для каждого сообщения:

List<byte> receiveBuffer = new List<byte>();
receiveBuffer.AddRange(new byte[] 
{
    0xFD, 1, 2, 0xFE,  
    0xFD, 1, 2, 3, 4, 5, 6, 7, 8, 0xFE, 
    0xFD, 33, 65, 25, 44, 0xFE
});

while (receiveBuffer.Count > 0)
{
    var bytesInRange = receiveBuffer.Skip(1).TakeWhile(n => n != 0xFE);

    foreach (var n in bytesInRange)
        Console.Write("{0} ", n);

    Console.WriteLine("\n");
    receiveBuffer.RemoveRange(0, bytesInRange.Count() + 2);
}
0 голосов
/ 19 мая 2010

идея такова:

while not the end of receiveBuffer 
 if receiverbuffer[actualposition] != 0xfe
    insert this position in a listA
  if receiverbuffer[actualposition] == 0xfe
    insert the listA into another listB
    listA become null and you go to next line
go to next position of receivebuffer

так что в конце процесса у вас будет список>

я надеюсь, что это не выглядит как большая путаница

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...