Delphi と C++ Builder 向け QUIC クライアント / サーバーコンポーネント | eSeGeCe ブログ

Delphi と C++ Builder 向け QUIC クライアント / サーバーコンポーネント

· コンポーネント
sgcQUIC QUIC クライアント / サーバーコンポーネント

sgcQUICQUIC トランスポートプロトコル (RFC 9000) を Delphi、C++ Builder、.NET にもたらします。TsgcQUICClientTsgcQUICServer という 2 つの新コンポーネントを提供し、どちらも OpenSSL 3.5 のネイティブ QUIC エンジン上に構築されているため、トランスポートと TLS 1.3 ハンドシェイクが 1 回のフライトとして実行されます。

QUIC は HTTP/3 の基盤となるプロトコルですが、それ単体でも有用です。UDP 上で動作し、1 本の接続上で多数の独立したストリームを多重化し、0-RTT で再開でき、接続マイグレーションのおかげでネットワークの切り替えにも耐えます。この投稿では生の QUIC レイヤーを取り上げます。次の投稿では、その上に載る HTTP/3 コンポーネントを取り上げます。

なぜ QUIC か

従来の TLS-over-TCP 接続では、アプリケーションデータの最初の 1 バイトが流れる前に TCP ハンドシェイクとそれに続く TLS ハンドシェイクが必要です。QUIC は両者を 1 回の交換にまとめるため、新規接続は 1 往復で準備が整い、再開された接続は 0-RTT でデータを送信できます。

各 QUIC ストリームはそれぞれ独立して配送されるため、あるストリームでのパケット損失が他のストリームを停滞させることはありません。これにより、1 本の TCP ソケット上で多重化したときに生じるヘッドオブラインブロッキングが解消されます。QUIC 接続は IP とポートのペアではなく接続 ID によって識別されるため、切断することなく Wi-Fi からモバイルデータへとマイグレーションできます。

QUIC クライアント

HostPort を設定し、イベントを割り当てたうえで Active を True に設定します。ハンドシェイクは 1 回のフライトで完了し、WriteData が QUIC ストリーム上にバイトを送信します。

uses
  sgcQUIC_Client;

var
  oClient: TsgcQUICClient;
begin
  oClient := TsgcQUICClient.Create(nil);
  oClient.Host := 'www.example.com';
  oClient.Port := 443;
  oClient.OnQUICConnect    := OnQUICConnect;
  oClient.OnQUICStreamData := OnQUICStreamData;
  oClient.OnQUICDisconnect := OnQUICDisconnect;

  oClient.Active := True;          // TLS 1.3 handshake in a single flight
  oClient.WriteData('ping');       // send bytes on a QUIC stream
end;

受信したバイトは OnQUICStreamData イベントに届き、ストリーム ID とペイロードを報告します:

procedure TForm1.OnQUICStreamData(Sender: TObject; aStreamId: Int64;
  aUnidirectional: Boolean; const aData: TBytes);
begin
  Memo1.Lines.Add('stream ' + IntToStr(aStreamId) + ': ' +
    TEncoding.UTF8.GetString(aData));
end;

QUIC サーバー

サーバーは UDP ポートで待ち受け、PEM 形式の証明書と秘密鍵を必要とします。QUIC は常に TLS 1.3 上で動作するため、証明書は必須です。クライアントのバイトは OnQUICData に届き、応答したい接続を渡して WriteData で返信します。

uses
  sgcQUIC_Server;

var
  oServer: TsgcQUICServer;
begin
  oServer := TsgcQUICServer.Create(nil);
  oServer.Port := 443;
  oServer.QUICOptions.SSLOptions.CertFile := 'cert.pem';
  oServer.QUICOptions.SSLOptions.KeyFile  := 'key.pem';
  oServer.OnQUICConnect := OnQUICConnect;
  oServer.OnQUICData    := OnQUICData;
  oServer.Active := True;
end;

procedure TForm1.OnQUICData(Sender: TObject;
  aConnection: TsgcQUICServerConnection; const aData: TBytes);
begin
  // echo the message back to the client that sent it
  oServer.WriteData(aConnection, aData);
end;

多重化ストリーム

1 本の QUIC 接続で同時に多数のストリームを運べます。接続する前に EnableMultiStream を呼び出し、必要な数だけストリームを開きます。双方向ストリームはリクエストとその応答を運び、単方向ストリームはデータを一方向に送ります。

oClient.EnableMultiStream;
oClient.OpenStream;                 // a new bidirectional stream
oClient.OpenStream(True);           // a unidirectional stream

マルチストリームモードは組み込みの OpenSSL 3.5 QUIC エンジンを使用します。各ストリームはそれぞれ独立してフロー制御されるため、遅いストリームや停滞したストリームが残りをブロックすることはありません。

0-RTT と接続マイグレーション

最初のハンドシェイクが成功した後、クライアントはセッションチケットを保持して 0-RTT で再接続でき、最初のパケットでアプリケーションデータを送信できます。接続マイグレーションはネットワークの切り替えをまたいでセッションを維持し、これは Wi-Fi とセルラーの間を移動するモバイルクライアントにとって重要です。どちらも基盤となる QUIC エンジンによって処理されます。

提供形態

sgcQUIC は独立したパッケージで、sgcWebSockets Enterprise のアドオンとしてライセンス供与されます。すべてのライセンスには完全な Pascal ソースコードと 1 年間のアップデートが含まれます。コンポーネントは SGC QUIC パレットにインストールされ、Delphi と C++ Builder の 7 から 13 まで、さらに同じ Tsgc クラス名を持つ .NET ポートに対応します。

HTTP/3 クライアント / サーバーコンポーネントについては次の投稿で取り上げます。トライアルのダウンロードと完全なコンポーネントリファレンスは sgcQUIC 製品ページでご覧いただけます。

ご質問やご意見はありますか? お問い合わせください。コードを書いた本人から返信いたします。