次の C# 関数を作成しました。
static string ReadLineCRLF(System.IO.Stream Stream, ref byte[] sharedBuffer, int bufferSize = 1024)
{
StringBuilder responseString = new StringBuilder("");
string returnString = "";
byte[] buffer = new byte[bufferSize];
bool stopreading = false;
bool firstexecution = true;
while (!stopreading)
{
int readBytes;
if (firstexecution && sharedBuffer.Length > 0)
{
readBytes = sharedBuffer.Length;
sharedBuffer.CopyTo(buffer, 0);
}
else
{
readBytes = Stream.Read(buffer, 0, bufferSize); //BLOCKING HERE
}
firstexecution = false;
if (readBytes > 0)
{
int crIndex = Array.IndexOf(buffer, (byte)13); //13 = ASCII value for a carriage return
if (crIndex > -1 && Array.IndexOf(buffer, (byte)10, crIndex + 1) == crIndex + 1) //10 = ASCII value for line feed
{
stopreading = true;
sharedBuffer = readBytes - crIndex - 2 > 0 ? ArraySlice<byte>(buffer, crIndex+2, readBytes-crIndex-2) : new byte[] { };
readBytes = crIndex;
}
if (readBytes > 0)
{
responseString.Append(System.Text.Encoding.ASCII.GetString(buffer, 0, readBytes));
}
if (stopreading)
{
returnString = responseString.ToString();
}
}
if (!stopreading && readBytes <= 0)
{
returnString = null;
stopreading = true;
sharedBuffer = new byte[] { };
}
}
return returnString;
}
readBytes = Stream.Read(buffer, 0, bufferSize);
私のStack Explorerによると、この機能はブロックされており、多くのコンピューターのパフォーマンスを使い果たしています。この関数が行うべき唯一のことは、CRLF ("\r\n") だけで終了するストリームから 1 行を読み取ることです。
MSDN Stream.Read
の返品によると、less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
通常はブロックされ、CPU パフォーマンスを使い果たすことはありません。The implementation will block until at least one byte of data can be read, in the event that no data is available. Read returns 0 only when there is no more data in the stream and no more is expected (such as a closed socket or end of file)
.
では、私の CLR Stack Explorer によると、なぜこれほど多くのパフォーマンス (最大 70%) を消費するのでしょうか? 論理的な間違いは見当たりません。何バイトか受信できるようになるまで待つべきだと思います。また、この動作は常に発生するわけではなく、Windows サーバーでアプリケーションの実行を開始してから 1 日または 2 日後に発生するようです。
補足説明: バイトはチャンクを使用して読み取られるため、読み取られてバッファに格納されるバイトが多すぎる可能性があります。したがって、共有バッファーを使用して、次の行の読み取りを続行できるようにします。行が完全に読み取られたら、バッファーから削除します。
ArraySlice 関数は次のようになります。
public static T[] ArraySlice<T>(T[] data, int index, int length)
{
T[] result = new T[length];
Array.Copy(data, index, result, 0, length);
return result;
}