HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Extracting complete lines from a data stream

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
streamcompleteextractingfromdatalines

Problem

When reading from the Console, it's very easy to get a complete line, you simply call Console.Readline.

When reading from a TCPClient there isn't an equivalent function. You can either read a byte at a time, or read a block (up to a maximum size) in which case an arbitrary amount of data will be returned depending how the network behaved.

In order to simplify line extraction I've written a LineBuffer class. It has an Append method that allows new blocks of data to be added to the buffer. Whenever a complete line is received, the action supplied via the constructor is called.

The LineBuffer class:

using System;
using System.Text;

namespace MudCore.Connection
{
    public class LineBuffer
    {
        private readonly Action _onLineFound;
        private readonly StringBuilder _currentLine;

        public LineBuffer(Action onLineFound)
        {
            _onLineFound = onLineFound;
            _currentLine = new StringBuilder();
        }

        public void Append(string input)
        {
            if (input == null) return;

            while (input.Contains("\n"))
            {
                var indexOfNewLine = input.IndexOf('\n');
                var left = input.Substring(0, indexOfNewLine);
                _currentLine.Append(left);

                var line = _currentLine.Replace("\r","").ToString();

                _currentLine.Clear();
                if (indexOfNewLine != input.Length - 1)
                {
                    input = input.Substring(indexOfNewLine + 1);
                }
                else
                {
                    input = string.Empty;
                }

                _onLineFound.Invoke(line);
            }

            if (!string.IsNullOrEmpty(input))
            {
                _currentLine.Append(input);
            }
        }
    }
}


Some unit tests:

```
using System;
using System.Collections.Generic;
using System.Collections;

using NUnit.Framework;

using MudCore.Connectio

Solution

There are some alternatives that are built in, for example in the simplest form combine a NetworkStream with StreamReader:

using (var netStream = new NetworkStream(tcpClient.Client))
using (var reader = new StreamReader(netStream))
{
    var line = reader.ReadLine();
}


Which is unbuffered, if you want to add buffering in, just use a BufferedStream in the middle:

using (var netStream = new NetworkStream(tcpClient.Client))
using (var bufferStream = new BufferedStream(netStream))
using (var reader = new StreamReader(bufferStream))
{
    var line = reader.ReadLine();
}


These are pretty high performance because they operate at a lower level. Ideally you'd want to ditch the TcpClient and go direct with a Socket for best performance, but TcpClient.Client gives direct access to the underlying socket.

Code Snippets

using (var netStream = new NetworkStream(tcpClient.Client))
using (var reader = new StreamReader(netStream))
{
    var line = reader.ReadLine();
}
using (var netStream = new NetworkStream(tcpClient.Client))
using (var bufferStream = new BufferedStream(netStream))
using (var reader = new StreamReader(bufferStream))
{
    var line = reader.ReadLine();
}

Context

StackExchange Code Review Q#147935, answer score: 4

Revisions (0)

No revisions yet.