有效地分割来自流的分隔符分隔的消息
本文关键字:分隔符 消息 分隔 分割 有效地 | 更新日期: 2023-09-27 17:52:46
我想要一种基于分隔符拆分传入数据流的内存高效和时间高效的方法。该流是一个网络流,进入的"消息"由CRLF
分割。以前,我通过使用UTF8将传入的数据转换为字符串来完成此操作,然后检查CRLF,如果存在,则基于此进行分割,然而,随着越来越多的消息传入,这不是解决问题的好方法。此外,我可能会得到包含1条消息的数据块,也可能得到包含10条消息的数据块,甚至有些数据块只包含消息的一部分。
这就是我到目前为止所想到的。使用内存流作为缓冲区,当数据传入时,将数据读入内存流。如果我找到了分隔符(CRLF),我将获取内存流中的所有数据,并在其上调用messagerreceived,然后继续。对此有什么想法吗?
[编辑]
好吧,我想我需要更好地解释我想做什么。使用的协议是irc协议,它发送"消息",或者"命令",如果你愿意,用CRLF
分隔。我在c#中使用BeginReceive和EndReceive的套接字类,所以一切都是异步运行的。我正在编写的类称为MessageConnection。它从tcp套接字接收数据,每当找到给定的分隔符(在本例中为CRLF
)时,我希望它调用一个名为OnMessage的函数,该函数将接收到的消息作为参数。在使用StringBuilder作为缓冲区之前,我已经解决了完全相同的问题,并在接收到数据时将新字符串附加到StringBuilder,然后根据分隔符拆分StringBuilder返回的字符串,清空StringBuilder,并插入拆分操作的最后一部分。之后,我循环遍历拆分数组(没有最后一个元素)并调用OnMessage。这不管感觉像是一种解决问题的有效办法,因为我做了很多从字符串转换——这是说不是verry好,所以我在想,需要有一种简单的方式来解决这个不用想在字符串,在字节数组,只有转换为一个字符串,当我有一个字节数组,实际代表了一个"消息",这就是我想要的帮助。
我认为你的想法是对的。只要用字节数组就行了。
我是这样做的,完全未经测试,可以优化....
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
{
int i = 0;
for(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();
}
前段时间我不得不做这样的事情。我通过创建生产者/消费者流解决了这个问题。生产者(在您的示例中,读取网络流的东西)向流写入字节,消费者创建连接到流的StreamReader
。
当然,这需要为消费者提供另一个线程,但它可以防止如果回调花费太长时间而最终丢失消息可能发生的问题。
我写了这个流,在一篇文章中我称之为ProducerConsumerStream
。参见http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=852。
以前解决这个问题的方法是自己解析字节数组。这种方法是有效的,但不如这种流方法灵活。