8

NetworkStreamデータをランダムに送信するものを読み取る必要があり、データパケットのサイズも変化し続けます。各スレッドが独自のストリームを読み取るマルチスレッド アプリケーションを実装しています。ストリームにデータがない場合、アプリケーションはデータが到着するまで待機し続ける必要があります。ただし、サーバーがデータの送信を完了し、セッションを終了した場合は、終了する必要があります。

最初はこのReadメソッドを使用してストリームからデータを取得していましたが、以前はスレッドをブロックし、データがストリームに現れるまで待機し続けていました。

MSDN のドキュメントでは、

読み取りに使用できるデータがない場合、Read メソッドは 0 を返します。リモート ホストが接続をシャットダウンし、使用可能なデータがすべて受信されている場合、Read メソッドはすぐに完了し、0 バイトを返します。

しかし、私の場合、Read0 を返して正常に終了するメソッドを取得したことがありません。無期限に待つだけです。

さらに調査したところBeginRead、ストリームを監視し、データを受信するとすぐにコールバック メソッドを非同期で呼び出すことがわかりました。このアプローチを使用してさまざまな実装を探してみましたがBeginReadRead.

私が見ているようにBeginRead、現在のスレッドをブロックしない非同期呼び出しがあるという利点があります。しかし、私のアプリケーションでは、ストリームからデータを読み取って処理するための別のスレッドが既にあるため、それほど大きな違いはありません。

  • BeginReadの待機と終了のメカニズムと、それとの違いを理解するのを手伝ってもらえます Readか?

  • 必要な機能を実装するための最良の方法は何ですか?

4

4 に答える 4

12

を使用BeginReadしていますが、次を使用してスレッドをブロックし続けていますWaitHandle

byte[] readBuffer = new byte[32];
var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, 
    null, null);

WaitHandle handle = asyncReader.AsyncWaitHandle;

// Give the reader 2seconds to respond with a value
bool completed = handle.WaitOne(2000, false);
if (completed)
{
    int bytesRead = stream.EndRead(asyncReader);

    StringBuilder message = new StringBuilder();
    message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead));
}

基本的に、 を使用して非同期読み取りのタイムアウトを許可し、設定された時間内に読み取りが完了した場合 (この場合) WaitHandle、ブール値 ( ) を返します。completed2000

以下は、私の Windows Mobile プロジェクトの 1 つからコピーして貼り付けた完全なストリーム読み取りコードです。

private static bool GetResponse(NetworkStream stream, out string response)
{
    byte[] readBuffer = new byte[32];
    var asyncReader = stream.BeginRead(readBuffer, 0, readBuffer.Length, null, null);
    WaitHandle handle = asyncReader.AsyncWaitHandle;

    // Give the reader 2seconds to respond with a value
    bool completed = handle.WaitOne(2000, false);
    if (completed)
    {
        int bytesRead = stream.EndRead(asyncReader);

        StringBuilder message = new StringBuilder();
        message.Append(Encoding.ASCII.GetString(readBuffer, 0, bytesRead));

        if (bytesRead == readBuffer.Length)
        {
            // There's possibly more than 32 bytes to read, so get the next 
            // section of the response
            string continuedResponse;
            if (GetResponse(stream, out continuedResponse))
            {
                message.Append(continuedResponse);
            }
        }

        response = message.ToString();
        return true;
    }
    else
    {
        int bytesRead = stream.EndRead(asyncReader);
        if (bytesRead == 0)
        {
            // 0 bytes were returned, so the read has finished
            response = string.Empty;
            return true;
        }
        else
        {
            throw new TimeoutException(
                "The device failed to read in an appropriate amount of time.");
        }
    }
}
于 2010-12-08T15:12:10.640 に答える
5

非同期 I/O を使用すると、より少ないスレッドで同じ量の I/O を実現できます。

お気づきのとおり、現在、アプリにはストリームごとに 1 つのスレッドがあります。接続数が少ない場合はこれで問題ありませんが、一度に 10000 をサポートする必要がある場合はどうでしょうか。非同期 I/O では、読み取り完了コールバックにより、関連するストリームを識別するコンテキストを渡すことができるため、これは不要になりました。読み取りがブロックされなくなったため、ストリームごとに 1 つのスレッドは必要ありません。

同期または非同期 I/O を使用するかどうかに関係なく、関連する API リターン コードでストリーム クローズダウンを検出して処理する方法があります。 ソケットが既に閉じられている場合、BeginReadは IOException で失敗するはずです。非同期読み取りが保留中の場合にクローズダウンすると、コールバックがトリガーされEndRead、プレイの状態が通知されます。

アプリケーションが BeginRead を呼び出すと、システムはデータが受信されるかエラーが発生するまで待機します。その後、システムは別のスレッドを使用して指定されたコールバック メソッドを実行し、提供された NetworkStream がデータを読み取るか例外をスローするまで EndRead をブロックします。

于 2010-12-08T15:21:46.087 に答える
0

server.ReceiveTimeout を試しましたか? Read() 関数がゼロを返す前に受信データを待機する時間を設定できます。あなたの場合、このプロパティはおそらくどこかで無限に設定されています。

于 2011-07-28T21:10:40.710 に答える
0

BeginRead は非同期プロセスです。これは、メイン スレッドが別のプロセスで Read の実行を開始することを意味します。これで、2 つの並列プロセスができました。結果を取得したい場合は、EndRead を呼び出す必要があります。これにより、結果が得られます。

いくつかの疑似

BeginRead()
//...do something in main object while result is fetching in another thread
var result = EndRead();

しかし、メイン スレッドに他に何もすることがなく、結果が必要な場合は、Read を呼び出す必要があります。

于 2010-12-08T15:24:46.993 に答える