Search code examples
c#ircnetworkstream

IRC using NetworkStream - buffer fills and line gets chomped


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

Problem is that even with a buffer as big as short.MaxValue, you can actually fill the buffer. When you split the string that you create from the buffer, the last string gets chomped, and the rest of it comes with the next read.

I was thinking of creating a buffer large enough for a single line (which according to RFC2812 is 512 chars), extracting a substring up until the first "\r\n", then array-copying the rest of the data to the beginning of the buffer and using the offset parameter to read more data onto the end of the data that wasn't extracted last iteration. Sorry if that was hard to follow...

Is that the best solution, or am I missing the obvious here?


Solution

  • So here's how I ended up solving it:

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