2

C# で作成しているプロキシ サーバーがあります。この C# プロキシを介してビデオ サーバーから MJPEG データを受信する Java アプレットもあります。私の問題は、現在ビデオ サーバーから利用できる MJPEG データがなくなると、プロキシがブロック読み取り呼び出しでスタックし、それを終了できないことです。

// write the forwarded output
// blocking on remoteServerResponseStream.Read
while (m_running && (read = remoteServerResponseStream.Read(buffer, 0, buffer.Length)) > 0)
{
    bytesRead += read;

    output.Write(buffer, 0, read);

    output.Flush();
}

これは、ストリームを閉じる Java アプレットによって終了する必要があります (output上記のコードの変数)。remoteServerResponseStream.Readただし、Java アプレットはこの接続を閉じることができません。これは、プロキシがデータ待ちで立ち往生している間、閉じる要求を認識しないためです。

私はこの問題に1週間立ち往生しています。解決策を考えたかもしれないと思いますが、うまくいくかどうかはわかりません。これに関するフィードバックをお待ちしております。

私の考えは、removeServerResponseStream.Read別のスレッドを使用し、共有キューを使用してデータを転送することでした。スレッドはデータを読み取り、それをキューに入れます。次に、メイン スレッドは、キュー内の利用可能なデータを に転送しますoutput。このようにして、 output.CanWritefalse になるかどうかを常に確認できます。その場合は、読み取りスレッドを中止できます (ブロッキング ストリームの読み取りを中断する唯一の方法を知っています)。これは実行可能な解決策ですか?その場合、使用可能なデータについてキューを常にポーリングする必要がありますか、それともイベントを作成する必要がありますか? この問題についての考えを聞きたいです! 前もって感謝します。

4

1 に答える 1

2

このような状況では、別のスレッドからストリーム リーダーを終了するだけです。リーダーを閉じると、ループが壊れます。余分な同期キューを通過することは、不必要に行うボイラープレートが増えるだけです。最終的にどこかで、ブロッキング リーダーを停止する必要があります。値をキューに入れると、コンシューマーがブロックされるのを防ぐだけで、リーダーはブロックされません。

以下は、ソケット ブロッキング読み取りの例です。意図的に送信しない 1 バイトの読み取りをブロックします。数秒後、ソケットを破棄します。ソケットはブロッキング読み取りから解放され、アプリは正常に終了します。各スレッドが何を行っているか、いつ発生したかをログに記録します (各ログ行の前に付いている数字はスレッド ID であり、個別のスレッドをインデントします)。

ブロッキング リーダーを使用するクラスでは、Disposable パターンを実装し、そこでリーダーを閉じる/破棄する必要があります。

static void Main(string[] args)
{
    SocketTest();

    Console.WriteLine("Press any key to exit");
    Console.ReadKey();
}

public static void SocketTest()
{
    int port = 22345;

    var tcpListener = new TcpListener(IPAddress.Any, port);

    tcpListener.Start();

    // Listening thread
    new Thread(() =>
    {

        Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Waiting for connection to port");

        var socket = tcpListener.AcceptSocket();

        Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Connection accepted");

        var stream = new NetworkStream(socket);
        var reader = new BinaryReader(stream);

        try
        {
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Starting blocking read");
            var bytes = reader.ReadBytes(1);
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId + " - Done blocking read, read {0} bytes", bytes.Length);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error reading " + ex);
        }
    }).Start();

    // connecting thread
    new Thread(() =>
    {
        var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Connecting to local port");

        socket.Connect("127.0.0.1", port);

        Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Connecting to local succeeded");

        Thread.Sleep(TimeSpan.FromSeconds(2));

        Console.WriteLine("\t" + Thread.CurrentThread.ManagedThreadId + " - Disposing of socket");

        socket.Dispose();

    }).Start();

    Thread.Sleep(TimeSpan.FromSeconds(5));
}

これを実行すると、次のようになります。

3 - Waiting for connection to port
        4 - Connecting to local port
        4 - Connecting to local succeeded
3 - Connection accepted
3 - Starting blocking read
        4 - Disposing of socket
3 - Done blocking read, read 0 bytes
Press any key to exit

あなたはここで質問に答えました:

私の考えは、別のスレッドで removeServerResponseStream.Read を持つことでした

これはまさにあなたがすべきことです。アプリが閉じていることがわかったら、アクティブなスレッドからソケットを閉じます。これでブロックされたスレッドが解放され、正常に終了できます。

ここでのパターンは、通常、特定のソケットのスレッドをスピンアップし、要求用の一種の「コントローラー」スレッドである 1 つのアクティブなスレッドを維持することです。

于 2012-09-04T19:00:57.257 に答える