title: Reading asyncronously for unknown data lengths
body: <p>I want to achieve an asynchronous read that reads from a <code>Stream</code> until a specific terminating byte has been found. I do not really know how long I have to read until the given byte is found and I would like to avoid having a thread wait on the result. Other things will later read the same stream, which implies that I can't read too far ahead after the terminating byte.</p>
  
  <p>So far I've managed this using the <code>BeginRead</code> and <code>EndRead</code> function pairs and a recursive <code>AsyncCallback</code>. This works well, but I'm a bit concerned how this will scale if the length is very long since it sometimes generates quite an impressive callstack if all the reads complete syncronously.</p>
  
  <pre><code>public void ReadUntilSpecificByte(Stream stream, byte terminator, Action&lt;byte[]&gt; onComplete)
  {
      var output = new List&lt;byte&gt;();
      var buffer = new byte[1];
      Action[] readAction = { null };
      readAction[0] = () =&gt; stream.BeginRead(buffer, 0, 1, asyncResult =&gt;
          {
              stream.EndRead(asyncResult);
              if (buffer[0] == terminator)
              {
                  onComplete(output.ToArray());
              }
              else
              {
                  output.Add(buffer[0]);
                  readAction[0]();
              }
          }, stream);
      readAction[0]();
  }
  </code></pre>
  
  <p>Am I worrying about something which is likely to be a non-issue, or can this be written in a smarter way that will run in a more contained amount of stack space? Unfortunately, I can't use c# 4.5, which I assume would make this task a whole lot smoother. The stream itself is typically a <code>NetworkStream</code> or a <code>SslStream</code>. </p>
  
  <p><strong>Edit:</strong> I've went with the approach suggested by Marc and did an internal buffer for all reads, keeping to a 2k buffer. It's a working solution that probably makes better use of potentially expensive reads since it'll always read as much as it can from the underlying socket. Still would have been cool if one could avoid that, but I guess it's part of the price of being totally async.  </p>