TsgcWebSocketClient › イベント › OnQueueDrained
送信メッセージキューが、メッセージを保持している状態から空になったときに発生します。
property OnQueueDrained: TsgcWSQueueDrainedEvent;
// TsgcWSQueueDrainedEvent = procedure(Connection: TsgcWSConnection) of object
—
メッセージキューイングが有効な場合、つまり QueueOptions.Text.Level、QueueOptions.Binary.Level、QueueOptions.Ping.Level のいずれかに qmNone 以外が設定されている場合にのみ発生します。キューがなければ排出するものがないため、デフォルトの qdmTransition モードではこのイベントは発生しません。
このハンドラーは接続スレッド上でインラインに実行され、このコンポーネントの他のすべてのイベントとは異なり、意図的に NotifyEvents を経由してマーシャリングされません。そのため、ユーザーインターフェイスに触れてはいけません。この相違は意図的なものです。このイベントの価値は、接続ループのまさにその時点、つまりキューが空になった直後、次の読み取りの前、ソケットがまだアイドル状態のときに実行されることにあります。これにより、アプリケーションは待つことなく次のチャンクを送出できます。メインスレッドへマーシャリングすると、この利点は失われます。フォーム、グリッド、ラベルの更新は、TThread.Queue、TThread.Synchronize、またはメインスレッドへ送るメッセージを使って、アプリケーション自身がマーシャリングする必要があります。
ハンドラー内で発生した例外は捕捉されて破棄されるため、排出処理は常に完了します。ハンドラーが失敗しても通知はされません。失敗を報告する必要がある場合は、本体を独自の try..except ブロックで囲んでください。
QueueOptions.DrainedMode プロパティが、イベントをいつ発生させるかを選択します。qdmTransition(デフォルト)では、後述のとおり、空でない状態から空になる遷移ごとに 1 回発生します。qdmEveryPass では、キューがすでに空であっても、また Text.Level、Binary.Level、Ping.Level がすべて qmNone であっても、キューの送信直後の接続ループのパスごとに発生するため、ダミーのキューレベルは不要です。このモードでは、ハンドラーは接続スレッド上の接続単位のフックとなり、たとえば WriteData で保留中のアプリケーションデータを送信すれば、接続へのすべての書き込みをその接続自身のスレッドから行えます(Binary.Level が qmNone の場合、イベント内の WriteData はソケットに直接書き込みます)。遷移モードと同様にインラインで実行され、NotifyEvents に従わないため、ハンドラーは高速でなければならず、ユーザーインターフェイスに触れてはいけません。各接続は接続時にキューオプションをコピーするため、DrainedMode はコンポーネントをアクティブにする前に設定してください。すでに開いている接続は以前のモードのままです。
qdmTransition モードでは、このイベントは接続ループのパスごとではなく、キューが空でない状態から空になる遷移ごとに 1 回発生します。バーストが終了したことを示す信号ではありません。排出が低速なプロデューサーに追いついている場合は、次のメッセージがキューに入る前に各メッセージが書き出されるため、1 通ごとにイベントが発生することもあります。このイベントが示すのは、この接続のキューが現時点で空であるということだけです。
代表的な用途はフロー制御です。クライアントが大量のデータをストリーミングする場合、このイベントを使って次のチャンクを送出すると、プロデューサーはメモリを埋め尽くすのではなくソケットの速度に追従します。TsgcWSConnection の PendingCount プロパティ(その接続でまだキューに残っているメッセージ数を返します)と組み合わせると、ストップアンドウェイト方式ではなくクレジットウィンドウ方式を実装できます。
マネージド .NET 版も同じ意味の同じイベントを発生させます。唯一の違いは発生する頻度です。Delphi の排出処理は接続ループのパスごとに実行され、その間隔は Options.ReadTimeOut ミリ秒(デフォルトは 10)です。そのため、ゆっくりとしたメッセージの流れはまとめられ、イベントの数は少なくなります。マネージド版の排出処理はイベント駆動であり、同じ流れでもより多くのイベントが発生することがあります。バーストの場合の動作は両者で同一です。
procedure OnQueueDrained(Connection: TsgcWSConnection);
begin
// runs on the connection thread, do not touch the user interface here
Connection.WriteData(GetNextChunk);
end;
// qdmEveryPass: runs on every pass of the connection loop
oClient.QueueOptions.DrainedMode := qdmEveryPass;
oClient.QueueOptions.Ping.Level := qmLevel1;
procedure OnQueueDrained(Connection: TsgcWSConnection);
begin
// runs on the connection thread, do not touch the user interface here
if Connection.Disconnected then
exit;
if HasPendingData then
Connection.WriteData(GetPendingData);
end;