使用LINQ,需要帮助在从Silverlight套接字接收的数据上拆分字节数组

时间:2010-05-18 19:57:08

标签: linq sockets network-programming parsing split

收到的消息packats包含多个消息,这些消息由header = 0xFD和footer = 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个字节,a和第3条消息包含6个字节。

在while循环中,我期待TakeWhile添加不等于消息的页脚部分的字节。

注意:由于我在读取它们之后删除了字节,因此总是可以预期标题位于“0”位置。

我搜索了分裂字节数组的示例,但未证明在未知和波动大小的数组上进行拆分。

任何帮助将不胜感激。 非常感谢!

4 个答案:

答案 0 :(得分:3)

真正棘手的部分是套接字是数据流,因此您的缓冲区实际上可能只包含部分消息。我有代码here实现一个基于单字节分隔符的“成帧器”,正确处理部分读取。它完全经过了单元测试。

请注意以下几十年经验的“框架”课程设计技巧:

  • 将您的邮件缓冲分成一个类。缓冲代码足够复杂,无需处理异步套接字。套接字事件处理程序可以负责保持异步读取一直进行,处理0长度读取和错误处理。然后他们应该将数据传递给缓冲类,缓冲类负责进行实际的框架。
  • 在编写消息缓冲类时,如果改变了对数据的思考方式,最终会得到更清晰的代码。而不是一大堆数据到达套接字并被“推送”通过缓冲类,而不是将其视为缓冲类发出隐含的“读取请求”;当一大块数据进入时,执行一个满足当前“读取请求”的循环,直到该块全部用完为止。

答案 1 :(得分:0)

这个想法是:

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

所以在流程结束时,你会有一个列表&gt;

我希望它看起来不那么混乱

答案 2 :(得分:0)

不确定这是否只是演示代码中引入的错误,但在从缓冲区中删除上一条消息时,您需要在计数中添加一条错误:

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);
}

答案 3 :(得分:0)

既然你已经有了适当的框架,那么Daniel的解决方案是否适合你呢?

如果您只想使用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();

然而,我真的觉得Daniel的解决方案更具可读性和可维护性。 Beware the dark side.

如果你坚持使用LINQ,我建议你写一个Partition扩展方法来清除所有这些,所以你可以拥有像receiveBuffer.Where(x => x != 0xFD).Partition(0xFE)这样的代码。目前还没有这样的功能。