ストリームから1行読み取る

ストリームから1行読み取りたいなら、StreamReader なんかが使えるわけだけど、文字列としてしか扱えないので不便だ。HTTP 通信ではヘッダまでは ASCII で読んで良いが、それ以降は画像だったり書庫だったりのデータが送られてくる事があるので、StreamReader では対応できない。生データのまま ReadLine できるクラスが必要なので作ってみた。
名前はてきとうに HttpHandler とかにしておく。

class HttpHandler
{
	Stream _stream;
	public HttpHandler(Stream stream)
	{
		_stream = stream;
	}

	public int Read(byte[] buffer, int offset, int length)
	{
		return _stream.Read(buffer, offset, length);
	}

	public string ReadLine()
	{
		byte[] line = ReadTo(new byte[] { 13, 10 });
		return Encoding.ASCII.GetString(line);
	}

	public byte[] ReadTo(byte[] terminator)
	{
		using (MemoryStream buffer = new MemoryStream())
		{
			int count = 0;
			int ch;
			while ((ch = _stream.ReadByte()) != -1)
			{
				buffer.WriteByte((byte)ch);
				if (ch == terminator[count])
				{
					if (count == (terminator.Length - 1))
						break;
					count++;
				}
				else
				{
					count = 0;
				}

			}
			return buffer.ToArray();
		}
	}
}

んで実行コード

private static void Run()
{
	TcpClient client = new TcpClient("www.google.co.jp", 80);
	BufferedStream stream = new BufferedStream(client.GetStream());

	// リクエスト送信
	byte[] request = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost: www.google.co.jp\r\nConnection: close\r\n\r\n");
	stream.Write(request, 0, request.Length);
	stream.Flush();

	HttpHandler handler = new HttpHandler(stream);
	byte[] headers = handler.ReadTo(new byte[] { 13, 10, 13, 10 }); // ヘッダの終わりまで読む
	Console.WriteLine(Encoding.ASCII.GetString(headers));

	// ボディを読む。Transfer-Encoding: chunked が前提。
	MemoryStream body = new MemoryStream();
	string line = handler.ReadLine();
	int chunkSize = int.Parse(line, System.Globalization.NumberStyles.HexNumber);
	byte[] receive = new byte[chunkSize];
	while (chunkSize > 0)
	{
		Console.WriteLine(chunkSize);
		int remain = chunkSize;
		int offset = 0;
		while (remain > 0)
		{
			int read = handler.Read(receive, offset, remain);
			offset += read;
			remain -= read;
		}
		body.Write(receive, 0, receive.Length);
		handler.ReadLine(); // Skip CRLF
		line = handler.ReadLine(); // next chunk size
		chunkSize = int.Parse(line, System.Globalization.NumberStyles.HexNumber);
		Array.Resize<byte>(ref receive, chunkSize);
	}

	line = handler.ReadLine(); // trailer
	while (!string.IsNullOrEmpty(line.Trim()))
	{
		Console.WriteLine(line);
		line = handler.ReadLine();
	}

	Console.WriteLine(Encoding.Default.GetString(body.ToArray()));

	body.Close();
	stream.Close();
	client.Close();
}

HttpHandler.ReadTo で指定した byte[] にマッチするまで読めます。
ReadByte() は遅いかもしれない。まぁヘッダ読むくらいにしか使ってないので気になりませんが。一応 BufferedStream を使って読んだ方がいいとおもう。