IRC с использованием NetworkStream - заполнение буфера и передача строки - PullRequest
1 голос
/ 24 августа 2010
var buffer = new byte[short.MaxValue];
var splitString = new string[] {"\r\n"};
while (_tcpClient.Connected)
{  
  if (!_networkStream.CanRead || !_networkStream.DataAvailable)
    continue;

  var bytesRead = _networkStream.Read(buffer, 0, buffer.Length);
  var stringBuffer = Encoding.ASCII.GetString(buffer, 0, bytesRead);
  var messages = 
     stringBuffer.Split(splitString, StringSplitOptions.RemoveEmptyEntries);
  foreach (var message in messages)
  {
    if (MessageReceived != null)
    {
      MessageReceived(this, new SimpleTextClientEventArgs(message));
    }
  }
}

Проблема в том, что даже с таким большим буфером, как short.MaxValue, вы можете заполнить буфер.Когда вы разбиваете строку, которую вы создаете из буфера, последняя строка разбивается, а остальная часть приходит со следующим чтением.

Я думал о создании буфера, достаточно большого для одной строки (согласно RFC2812 это 512 символов), извлекая подстроку до первого "\ r \ n", затем копируя в массив оставшиеся данные в начало буфера и используя параметр смещения, чтобы прочитать больше данных в конецданные, которые не были извлечены на последней итерации.Извините, если за этим трудно было следовать ...

Это лучшее решение, или я упускаю очевидное здесь?

Ответы [ 2 ]

1 голос
/ 24 августа 2010

Вы имеете дело с TCP / IP, что означает, что вы имеете дело с stream data.Вы не должны полагаться на то, как поступают данные, с точки зрения того, даст ли один вызов Read всю информацию целиком или нет.В таком случае вы, вероятно, захотите просто продолжить чтение (оно будет блокироваться, пока не будет данных) и найти преобразование двоичных данных в текстовый буфер.Когда вы видите терминатор строки в текстовом буфере, вы можете уведомить более высокий уровень этого сообщения и удалить его из буфера ... но не предполагайте ничего о том, что следует за этим сообщением.Возможно, у вас еще есть для чтения больше данных.

В качестве дополнительного примечания, действительно ли IRC только ASCII?Если так, то это, по крайней мере, немного упрощает ...

0 голосов
/ 24 августа 2010

Итак, вот как я это решил:

var buffer = new byte[Resources.MaxBufferSize];
var contentLength = 0;
while (_tcpClient.Connected)
{
  if (!_networkStream.CanRead || !_networkStream.DataAvailable)
    continue;

  var bytesRead = _networkStream.Read(buffer, contentLength, buffer.Length - contentLength - 1);
  contentLength += bytesRead;
  var message = string.Empty;
  do
  {
    message = ExtractMessage(ref buffer, ref contentLength);
    if (!String.IsNullOrEmpty(message))
    {
      if (MessageReceived != null)
      {
        MessageReceived(this, new SimpleTextClientEventArgs(message));
      }                        
    }
  } while (message != string.Empty);
}

private string ExtractMessage(ref byte[] buffer, ref int length)
{
  var message = string.Empty;
  var stringBuffer = Encoding.UTF8.GetString(buffer, 0, length);
  var lineBreakPosition = stringBuffer.IndexOf(Resources.LineBreak);
  if (lineBreakPosition > -1)
  {
    message = stringBuffer.Substring(0, lineBreakPosition);
    var tempBuffer = new byte[Resources.MaxBufferSize];
    length = length - message.Length - Resources.LineBreak.Length;
    if (length > 0)
    {
      Array.Copy(buffer, lineBreakPosition + Resources.LineBreak.Length, tempBuffer, 0, length);
      buffer = tempBuffer;
    }
  }
  return message;
}
...