この記事は、 Antonio Zapater氏によるblog記事「Server-Sent Events (SSE): Getting Real-Time Updates in Your Apps」の抄訳です。
ダッシュボードや操作画面を作成したことがある方なら、この問題はよくご存知でしょう。画面上の数値はサーバー上で実際に起こっていることを反映させたいものの、変更の有無に関わらず毎秒リクエストを送信してバックエンドに負荷をかけるポーリングループを実装したくはありません。もっと良い方法があるはずです。
更新頻度の高い多くのシナリオにおいて、より良い方法はServer-Sent Events (SSE)です。これは標準的なHTTPベースの方法で、サーバーが1つの接続を維持し、発生した更新をクライアントにストリーミング配信します。
RAD Studio 13.1 では、サーバー側とクライアント側の両方でこの機能に対するファーストクラスのサポートが追加されました。そこで、エンドツーエンドの動作を示すための簡単な概念実証を作成しました。Table of Contents
SSEとは?
SSEの仕組みはシンプルです。クライアントはサーバーとの通常のHTTP接続を確立し、接続を維持します。サーバーは通常のWebページのように応答を送信して接続を閉じるのではなく、接続を維持し、報告すべき事象が発生するたびにユーザーのブラウザまたはアプリケーションにデータをプッシュします。
クライアントは受信したチャンクを処理し、データに応じてUIを更新します。SSEが真価を発揮するのはまさにこのようなシナリオです。サーバーはクライアントが必要とする情報を把握しており、クライアントの役割は主にそれらを表示することです。リアルタイムのメトリクス、グラフデータ、ジョブの進捗状況、通知、最新のログなど…これらのいずれにおいても、SSEは最適なソリューションであり、完全なWebSocket構成よりもはるかに簡単に実装できます。
WebSocketは、初期コンテンツと同じチャネルで高頻度の双方向通信が必要な場合や、複雑な双方向プロトコルを使用するシステムを構築する場合に非常に便利です。
しかし、サーバーからクライアントへのアップデートに関しては、通常、SSEの方がよりクリーンで最適な選択肢となります。- プレーンなHTTPで動作し、特別なプロトコルは必要ありません。
- すべてのブラウザ、およびRAD StudioのTHTTPEventSourceに再接続機能が組み込まれています。
- 名前付きイベントタイプにより、クライアント側のロジックをシンプルかつ明確に記述できます。
RAD Studio における SSE の基本
RAD Studio 13.1 で SSE を動作させるために必要なコードを見ていきましょう。新しい System.Net.HttpSseユニットと WebBroker のアップデートのおかげで、内部処理は驚くほど簡単になりました。
サーバー側 (WebBroker)
標準 HTTP レスポンスをストリーミング SSE 接続に変換するには、TWebResponseStream.BeginEventsStream を使用してレスポンスを更新します。
ここからは、クライアントが接続されている限り、イベントをループで記述するだけです。procedure TWebModule1.WebModule1EventsAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
Stream: TWebResponseStream;
begin
Handled := True;
// 1. Upgrade the standard HTTP response to an active SSE stream
Stream := TWebResponseStream.BeginEventsStream(Response, 15);
try
while Stream.Connected do
begin
// 2. Write and flush a single event
Stream.WriteEvent('message');
Stream.WriteData('Hello from Delphi SSE!');
Stream.EndEvent;
// 3. Yield to keep the loop from eating the CPU
Sleep(100);
end;
except
on E: Exception do
; // Handle client disconnects gracefully
end;
end;
クライアント側 (THTTPEventSource)
クライアント側では、新しいTHTTPEventSourceを使用します。これはバックグラウンドでHTTP接続を処理し、受信イベントをキューに格納します。やるべき事は、キューからイベントを取り出し、データを読み取るだけです。
procedure TForm1.HandleSseMessages(ASender: THTTPEventSource);
var
Ev: THTTPEvent;
begin
// 1. Grab the next available event from the queue
Ev := ASender.GetEvent;
while Assigned(Ev) do
begin
try
// 2. Process the event payload
AppendLog(Format('Event: %s | Data: %s', [Ev.Event, Ev.Data.Text]));
finally
Ev.Free;
end;
// 3. Keep pulling until the queue is empty
Ev := ASender.GetEvent;
end;
end;
基本的な仕組みが理解できたところで、この基盤を基に、カスタマイズ可能なタイミング、バックグラウンドスレッド、複数のイベントチャネルを備えたより高度なシステムへと拡張する方法を見ていきましょう。
サーバーサイドイベント (SSE) デモ

このデモは、以下の3つの要素で構成されています。 Delphi WebBroker コンソールサーバー、同じサーバーから WebStencils を使用して配信されるブラウザ ダッシュボード ページ、そしてネイティブ FireMonkey デスクトップ アプリケーション。これら 3 つのプロセスはすべて同時に実行され、ブラウザ クライアントと FMX クライアントはどちらも同じ SSE エンドポイントに接続します。
サーバーは /events エンドポイントを公開します。クライアントが接続すると、サーバーはその接続を開いたままにして、名前付きイベントをループでプッシュし始めます。ストリームには 3 種類のイベントが流れます。
- ハートビート — ストリームを維持し、サーバーが稼働していることを確認するために、設定可能な間隔で実行されます。
- ダッシュボード — アクティブユーザー数、売上合計、平均注文額などのKPIのリアルタイムスナップショットを表示します。
- 進捗状況 — バックグラウンドジョブが実行されている場合に表示され、ジョブの進行状況に応じて更新されます。
具体的な動作を実現するのはジョブのトリガーです。どちらかのクライアントから「ジョブ開始」ボタンをクリックすると、両方のUIで同時に進捗状況の更新が受信されます。ブラウザのプログレスバーとFMXのプログレスバーは、どちらも同じサーバー側の状態を読み取っているため、同期して動きます。
SSE-Demo はサーバー上でどのように動作するのでしょうか?
サーバー側はWebBrokerを使用して構築されています。SSEエンドポイントは、TWebResponseStream.BeginEventsStreamを使用してストリーミングレスポンスを開き、接続が維持されている間はループ内で待機し、タイマーが経過するたびにイベントを発行します。各クライアントはクエリパラメータを使用して更新頻度を調整できるため、あるクライアントは1秒ごとにハートビートを要求し、別のクライアントは5秒ごとに要求することができます。
サーバーは値を適切な範囲に制限し、残りの処理を行います。これがその要点です。SSEストリームを開き、JSONペイロード(またはその他のデータ)を含む名前付きイベントを書き込み、フレームを終了します。
ダッシュボードとジョブの状態は、スレッドセーフなシングルトンオブジェクトのペアを介して、すべての接続で共有されます。
これが数値の一貫性を保つ理由です。ダッシュボードのスナップショットを要求するすべてのクライアントは、接続ごとにランダムに変化する同期ずれのない、同じ基となる値を取得します。クライアント側では、ブラウザは組み込みの EventSource API を使用します。FMX アプリケーションは、RAD Studio 13.1 で追加された System.Net.HttpSse の THTTPEventSource を使用します。どちらのクライアントも同じイベント名をリッスンし、イベントが発生すると UI 要素を更新します。サーバーは、クライアントがブラウザか Delphi アプリケーションかを認識したり、気にしたりする必要はありません。 イベントの形式はどちらの場合も同じです。
サーバーサイドイベントはDelphi固有の技術ではありません
ここで明確にしておきたいのは、SSEはオープンなWeb標準であるということです。RAD StudioはSSEを作成・使用するためのネイティブな方法を提供していますが、サーバーサイドイベントの基本的な動作形式は、あらゆるHTTPスタックと主要なブラウザで理解できます。つまり、DelphiサーバーはJavaScriptクライアント、Pythonクライアント、Goクライアントなど、HTTPに対応するあらゆるクライアントにイベントを送信できるということです。
同様に、THTTPEventSource を使用する Delphi アプリケーションは、特別な調整なしに、Delphi 以外のサービスから SSE ストリームを受信できます。最後の点は、実際に役立ちます。主要な公開されている本番環境の API の多くは、既に SSE 経由でレスポンスをストリーミングしています。
- OpenAI Responses API — ストリームモデルはトークンごとに出力します。
- Anthropic Messages API — Claude ストリーム は、stream: true で応答します。
- MCP Streamable HTTP transport — サーバーとクライアント間の通信におけるストリーミング部分にはSSEを使用します。
THTTPEventSourceを使用したDelphiアプリケーションは、SSEの結果を処理するためのカスタムHTTPストリーミングパーサーを作成することなく、これらのAPIに直接接続できます。
MCPはSSEとどのように連携するのでしょうか?
関連する重要なトピックがあります。モデルコンテキストプロトコル(MCP)は、AIツール、エディタ、エージェントを外部機能に接続するための共通標準となっています。当初のMCP HTTPトランスポートはSSE上に構築されていましたが、現在の仕様ではより柔軟なトランスポートであるStreamable HTTPへと移行しています。しかし、この新しいトランスポートも、通信のストリーミング部分には依然としてSSEを使用しています。
実際には、現在使用されている多くのMCP実装は依然としてSSEベースであり、比較的新しいものでさえ、同じストリーミングの基本原理に依存しています。ここで重要なのは、DelphiでMCP統合を構築したり、他社が開発しているサードパーティ製MCPサーバーに接続したりする場合、このデモで得られるストリーミングの知識がそのまま応用できるということです。HTTPストリーミングレイヤーをゼロから構築する必要はありません。このPoCで示されているのと同じTHTTPEventSource 仕組みを利用すれば良いのです。
次にやるべきことは?
このデモは意図的に小規模に作られています。ダッシュボードのKPIはランダムにシミュレーションされ、バックグラウンドジョブはタイマーです。重要なのはデータではなく、その仕組みです。
このデモで示すように、Delphi WebBrokerエンドポイントからブラウザページとFMXアプリケーションに同時にサーバー送信イベントがどのように流れるかを理解すれば、モックデータを実際のサービスからのビジネスイベントに置き換えるのは簡単な手順です。RAD Studioアプリケーションに、アーキテクチャの複雑さを大幅に増すことなくリアルタイム更新を追加する実用的な方法をお探しなら、これは最適な出発点となります。
Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.
Free Delphi Community Edition Free C++Builder Community Edition





