Эффективное разделение разделенных разделителем сообщений, поступающих в поток - PullRequest
4 голосов
/ 23 апреля 2011

Мне нужен эффективный для памяти и времени способ разделения входящего потока данных на основе разделителя.Поток является сетевым потоком, и поступающие «сообщения» делятся на CRLF.Ранее я делал это путем преобразования входящих данных в строку с использованием UTF8, затем проверял CRLF, и, если он существует, я делю его на основе этого, однако, это не очень хороший способ решения проблемы, так как все больше и больше сообщенийвходящие.Кроме того, я мог бы получить блоки данных, содержащие 1 сообщение, и я мог бы получить блоки данных, содержащие 10 сообщений, и даже те, которые содержат только части сообщений.

Так что это то, что я придумал до сих пор.Используйте поток памяти для буфера, и когда данные поступают, считывают данные в поток памяти.Если я нахожу разделитель (CRLF), я беру все данные в потоке памяти и вызываю сообщение «Получено», затем я продолжаю.Есть мысли по этому поводу?

[Редактировать]
Хорошо, я думаю, мне нужно лучше объяснить, что я хочу сделать.Используемый протоколл - это IRC-протоколл, который отправляет «сообщения» или «команды», если хотите, отделенный CRLF.Я использую класс сокетов в C # с BeginReceive и EndReceive, поэтому все работает асинхронно.Класс, который я пишу, называется MessageConnection.Он получает данные из tcp-сокета, и когда бы ни был найден данный разделитель (в данном случае CRLF), я хочу, чтобы он вызывал функцию OnMessage, которая принимает полученное сообщение в качестве параметра.Я решил точно такую ​​же проблему, прежде чем использовать StringBuilder в качестве буфера и добавлять новую строку в StringBuilder всякий раз, когда получал данные, затем я разделял строку, возвращенную StringBuilder, на основе разделителя, очищал StringBuilder ивставить последнюю часть операции split.После этого я делаю цикл через split-массив (без последнего элемента) и вызываю OnMessage.Это похоже на неэффективный способ решения проблемы, потому что я много конвертирую в и из строк - что, как говорят, не очень хорошо, поэтому я подумал, что должен быть простой способ решить эту проблему, не имеядумать в строках, только в байтовых массивах, и преобразовывать в строку только тогда, когда у меня есть байтовый массив, представляющий фактически все «сообщения», и это то, с чем я хочу помочь.

Ответы [ 2 ]

1 голос
/ 28 июля 2011

Я думаю, у вас действительно правильная идея.Просто сделайте это с байтовым массивом.

Вот как я бы это сделал, чисто не проверенный и мог бы быть оптимизирован ....

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();
}
0 голосов
/ 28 июля 2011

Я должен был сделать что-то подобное некоторое время назад. Я решил это, создав поток производителей / потребителей. Производитель (в вашем случае, вещь, которая читает сетевой поток) записывает байты в поток, а потребитель создает StreamReader, подключенный к потоку.

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

Я записал поток, который я назвал ProducerConsumerStream в статье. Посмотреть на http://www.informit.com/guides/content.aspx?g=dotnet&seqNum=852.

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

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