kbmMWを使用したWebSocketサーバーの実装手順

kbmMWの次期バージョンでは、WebSocketプロトコルのネイティブサポートが追加される予定です。このアップデートにより、kbmMWのフレームワーク上でリアルタイム通信を行うWebサーバーを容易に構築できるようになります。

サーバーの実装は非常に直感的です。まず、TkbmMWServerとトランスポート層であるTkbmMWWebSocketServerTransportを初期化し、必要なイベントハンドラを関連付けます。

constructor TMainForm.Create(AOwner: TComponent);
begin
  inherited;
  // サーバーコンポーネントの生成
  MWServer := TkbmMWServer.Create(nil);
  
  // WebSocketトランスポートの生成と設定
  WSTransport := TkbmMWWebSocketServerTransport.Create(nil);
  WSTransport.Server := MWServer;
  
  // イベントハンドラの割り当て
  WSTransport.OnWebSocketData := ProcessIncomingData;
  WSTransport.OnWebSocketOpen := HandleConnectionOpen;
  WSTransport.OnWebSocketClose := HandleConnectionClose;
  WSTransport.OnWebSocketPong := HandlePongResponse;
  
  // サービスの自動登録
  MWServer.AutoRegisterServices;
  
  // ログ出力の設定
  Logger.OutputToStrings(mmoLog.Lines);
end;

次に、サーバーの起動と停止、およびSSL/TLS通信の設定を行う処理を実装します。ここでは、SSLの有無によってポート番号や証明書のパスを動的に切り替えています。

procedure TMainForm.ToggleServerConnection(Sender: TObject);
begin
  if MWServer.Active then
  begin
    MWServer.Active := False;
    btnConnect.Caption := 'Start Server';
  end
  else
  begin
    WSTransport.Host := '0.0.0.0';
    
    if chkUseSSL.Checked then
    begin
      // SSL設定
      WSTransport.UseSSL := True;
      WSTransport.Port := 443;
      WSTransport.SetSSLCertificateFromFile('.\server.crt');
      WSTransport.SetSSLPrivateKeyFromFile('.\server.key');
    end
    else
    begin
      // 非SSL設定
      WSTransport.UseSSL := False;
      WSTransport.Port := 8080;
    end;
    
    MWServer.Active := True;
    btnConnect.Caption := 'Stop Server';
  end;
end;

以下のコードは、WebSocket通信における各種イベント(接続確立、切断、メッセージ受信、Pong応答)の具体的な処理ロジックです。

procedure TMainForm.ProcessIncomingData(const AConnection: IkbmMWWebSocketConnection; const AData: TValue);
var
  ReceivedText: string;
begin
  ReceivedText := AData.ToString;
  Logger.Info('Received data: ' + ReceivedText);
  
  // 受信したデータを全クライアントにブロードキャスト
  AConnection.BroadcastText(ReceivedText);
end;

procedure TMainForm.HandlePongResponse(const AConnection: IkbmMWWebSocketConnection; const AData: TValue);
begin
  Logger.Info('Pong received from ' + 
              AConnection.GetPeerAddr + ':' + IntToStr(AConnection.GetPeerPort) +
              ' Data: ' + AData.ToString);
end;

procedure TMainForm.HandleConnectionOpen(const AConnection: IkbmMWWebSocketConnection);
begin
  // 接続ごとに一意のタグを設定
  AConnection.TagString := 'UserSession_' + IntToStr(GetTickCount);
  Logger.Info('Connection opened: ' + 
              AConnection.GetPeerAddr + ':' + IntToStr(AConnection.GetPeerPort) +
              ' Tag: ' + AConnection.TagString);
end;

procedure TMainForm.HandleConnectionClose(const AConnection: IkbmMWWebSocketConnection; const ACode: Word; const AReason: string);
begin
  Logger.Info('Connection closed: ' + 
              AConnection.GetPeerAddr + ':' + IntToStr(AConnection.GetPeerPort) +
              ' Code: ' + IntToStr(ACode) + ' Reason: ' + AReason);
end;

最後に、kbmMWウィザードを利用してSmart Web Serviceを作成し、HTTPリクエストをWebSocket接続にアップグレードするための処理を追加します。

unit uWebSocketService;

interface

uses
  SysUtils, Classes, kbmMWSecurity, kbmMWServer, kbmMWServiceUtils,
  kbmMWHTTPStdTransStream, kbmMWCustomHTTPSmartService, kbmMWRTTI;

type
  [kbmMW_Service('flags:[listed]')]
  [kbmMW_Rest('path:/')]
  TWSGatewayService = class(TkbmMWCustomHTTPSmartService)
  private
    { Private 宣言 }
  protected
    { Protected 宣言 }
  public
    { Public 宣言 }
    
    [kbmMW_Rest('method:get, path:status')]
    [kbmMW_Method]
    function GetStatus: string;

    // HTTPプロトコルをWebSocketにアップグレードするメソッド
    function kbmMWCustomHTTPSmartServiceUpgrade(Sender: TObject;
      const ARequestHelper, AResponseHelper: TkbmMWHTTPTransportStreamHelper;
      const ARequestedProtocols: string;
      var AAcceptedProtocol: string): Boolean;
  end;

implementation

uses kbmMWExceptions;

function TWSGatewayService.GetStatus: string;
begin
  Result := 'Service is running';
end;

function TWSGatewayService.kbmMWCustomHTTPSmartServiceUpgrade(Sender: TObject;
  const ARequestHelper, AResponseHelper: TkbmMWHTTPTransportStreamHelper;
  const ARequestedProtocols: string; var AAcceptedProtocol: string): Boolean;
begin
  // プロトコルの受諾と設定
  AAcceptedProtocol := 'websocket';
  Result := True;
end;

initialization
  TkbmMWRTTI.EnableRTTI(TWSGatewayService);

end.

以上で、kbmMWを使用したWebSocketサーバーの基本的な構築は完了です。

6月20日 23:20 投稿