高效拆分分隔符分隔的流中的消息

时间:2011-04-23 01:56:44

标签: c# stream

我想要一种基于分隔符分割传入数据流的内存高效且省时的方法。流是网络流,进入的“消息”由CRLF分割。以前我已经通过使用UTF8将incomming数据转换为字符串,然后检查CRLF,如果它存在我基于此分割,但是,这不是解决问题的一种非常好的方法,因为越来越多的消息是传入。另外,我可能会得到包含1条消息的数据块,并且我可能会得到包含10条消息的datachunks,甚至还有一些只包含部分消息的消息。

所以这就是我到目前为止所想到的。将内存流用于缓冲区,当数据进入时将数据读入内存流。如果我找到了分隔符(CRLF),我会获取内存流中的所有数据,然后调用messageReceived,然后继续。有什么想法吗?

[编辑]
好吧,我想我需要更好地解释我想做什么。使用的协议是IRC-protocoll,它发送“消息”或“命令”,如果你想要,由CRLF分隔。我在C#中使用带有BeginReceive和EndReceive的套接字类,所以一切都运行异步。我正在编写的类称为MessageConnection。它从tcp-socket接收数据,每当找到给定的分隔符时(在这种情况下为CRLF),我希望它调用一个名为OnMessage的函数,将接收的消息作为参数。我在使用StringBuilder作为缓冲区之前解决了完全相同的问题,并在每次收到数据时将新字符串附加到StringBuilder,然后我根据分隔符拆分StringBuilder返回的字符串,清空StringBuilder,以及插入拆分操作的最后一部分。之后,我循环使用split-array(没有最后一个元素)并调用OnMessage。这种方式感觉就像是一种解决问题的低效方式,因为我对字符串进行了大量的转换 - 据说不是很好,所以我想,需要有一种简单的方法来解决这个问题而不必在字符串中思考,只在字节数组中,只有在我有一个表示实际“消息”的字节数组时才转换为字符串,这就是我想要的帮助。

2 个答案:

答案 0 :(得分:1)

我认为你确实有正确的想法。只需使用字节数组即可。

以下是我如何做,纯粹未经测试,可以优化....

byte[] m_LongBuffer;
byte[] m_SmallBuffer;
void ReceiveCallback(IAsyncResult iar)
{
   //m_SmallBuffer contains the data read from the stream
   //Append it to m_LongBuffer
   int bytesread = socket.EndReceive(iar);
   m_LongBuffer = m_LongBuffer.Concat(m_SmallBuffer.Take(bytesread)).ToArray();

   int startpoint = 0;
   int splitpoint = 0;
   int lastendpoint = 0;
   bool twochar = false;

   do
   {
       for(int i=0;i<m_LongBuffer.Length;++i)
       {
           if((m_LongBuffer[i] == 0x0A) || (m_LongBuffer[i] == 0x0D))
           {
               splitpoint = i;
               if((m_LongBuffer[i+1] == 0x0A) || (m_LongBuffer[i+1] == 0x0D))
                    twochar=true;
               else
                    twochar=false;

               lastendpoint = splitpoint;                   
               String message = ASCII.ASCIIEncoding.GetString(m_LongBuffer.Skip(startpoint).Take(splitpoint - startpoint).ToArray());
               //Do something with the message
               startpoint = splitpoint + (twochar ? 2 : 1);
               break;
           }
       }
       if(i >= m_LongBuffer.Length)
            splitpoint = -1;
   } while (splitpoint != -1);
   m_LongBuffer = m_LongBuffer.Skip(lastendpoint).ToArray();
}

答案 1 :(得分:0)

我不得不做一段时间这样的事情。我通过创建生产者/消费者流来解决它。生产者(在您的情况下,读取网络流的东西)将字节写入流,并且消费者创建连接到流的StreamReader

当然,这需要消费者使用另一个线程,但它可以防止在回调时间过长而你最终丢失消息时可能出现的问题。

我写了一个流,我在一篇文章中称之为ProducerConsumerStream。请参阅http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=852

此问题的先前解决方案涉及自己解析字节数组。这很有效,但不像这种流方法那么灵活。