【プロトコルUNITクラスの実装】
はじめに
このクラスは、イベントハンドラをキューとUNITという単位に分けたモデルでプロトコル実装におけるイベントハンドラを定義するためのもので、専用のコマンドを使ってスキャルフォールディングできます。
そして、あらかじめ予約されている接続要求/データ送受信/アライブチェック/切断などのキューに任意のUNITを割り当ててプロトコルを実装していきます。
※キューとUNITの詳細については▶イベントハンドラについてのページを参照。
プロトコルUNITクラスを生成するコマンドは以下の通り。
コマンドを実行する事で
以降では生成されたファイルの内容を見ていきます。
そして、あらかじめ予約されている接続要求/データ送受信/アライブチェック/切断などのキューに任意のUNITを割り当ててプロトコルを実装していきます。
※キューとUNITの詳細については▶イベントハンドラについてのページを参照。
プロトコルUNITクラスを生成するコマンドは以下の通り。
> php worker craft:protocol ProtocolForTest [success] プロトコルUNITクラスの生成に成功しました (ProtocolForTest) [success] プロトコルUNITのキュー名Enumの生成に成功しました (ProtocolForTestQueueEnum) [success] プロトコルUNITのステータス名Enumの生成に成功しました (ProtocolForTestStatusEnum)
コマンドを実行する事で
app/ProtocolUnits
の場所に以下3つのファイルが生成されます。- ・ProtocolForTest.php
-
IEntryUnits
インターフェイスがimplementsされたプロトコルUNITクラスです。
ここにプロトコル処理のイベントハンドラを実装します。
- ・ProtocolForTestQueueEnum.php
-
ここにプロトコル処理のキュー(イベント)名を定義します。
- ・ProtocolForTestStatusEnum.php
-
ここにプロトコル処理のステータス(UNIT)名を定義します。
以降では生成されたファイルの内容を見ていきます。
キュー名の定義
ProtocolForTestQueueEnum.php
のファイルは、イベントに対応するキュー名を定義するEnumファイルです。プロトコルUNITのキュー名はあらかじめ
ProtocolQueueEnum
で予約されていますので、生成されたEnumファイルにはその内容が代入されています。定義済みのキュー名は以下の通り。
- ProtocolForTestQueueEnum::ACCEPT
- クライアントからの接続要求に対するアクセプト時に呼ばれる
- ProtocolForTestQueueEnum::CONNECT
- 当該サーバーから他のエンドポイントへ接続する時に呼ばれる
- ProtocolForTestQueueEnum::RECV
- 通信データを受信する時に呼ばれる
- ProtocolForTestQueueEnum::SEND
- 通信データを送信する時に呼ばれる
- ProtocolForTestQueueEnum::CLOSE
- 切断シーケンスを走らせる時に呼ばれる
- ProtocolForTestQueueEnum::ALIVE
- アライブチェックを走らせる時に呼ばれる
今回のケースでは
ProtocolForTest
クラス内でキューとUNITの紐づけを行います。紐づけの方法は以下の>> プロトコルUNITクラスの実装の項で説明しています。
※必ずしも全てのキューを使う必要はありませんが、オリジナルプロトコルを開発する場合は少なくともSENDキューとRECVキューが必要になります。
ステータス名の定義
ProtocolForTestStatusEnum.php
のファイルは、プロトコルUNITのステータス名を定義するEnumファイルです。プロトコルUNITのステータス名であらかじめ予約されているものは
StatusEnum::START
だけで、生成されたEnumファイルにはその内容が代入されています。それ以外のステータス名は自由に定義する事ができます。
定義済みのステータス名は以下の通り。
- ProtocolForTestStatusEnum::START
-
同じキューに登録されているUNITの集合のうち一番最初に実行されるステータスです。
UNITは必ずSTARTステータスから始まるルールになっています。
ProtocolForTest
クラス内でUNITとステータス名の紐づけを行います。紐づけの方法は以下の>> プロトコルUNITクラスの実装の項で説明しています。
※ここで定義したステータス名は異なるキューで再利用が可能です。
プロトコルUNITクラスの実装
ProtocolForTest.php
のファイルにはUNIT定義を含めた各種メソッドが実装されています。各メソッドの仕様と実装例は以下の通り。
➤キューリストの取得
フレームワークは以下のメソッドをコールして必要なキューのリストを登録します。【メソッド】getQueueList(): array 【パラメータ】なし 【戻り値】array - キュー名のリスト
例えば予約されている全てのキューを利用する場合、以下のように実装します。
protected const QUEUE_LIST = [ ProtocolForTestQueueEnum::ACCEPT->value, // アクセプトを処理するキュー ProtocolForTestQueueEnum::RECV->value, // 受信処理のキュー ProtocolForTestQueueEnum::SEND->value, // 送信処理のキュー ProtocolForTestQueueEnum::CLOSE->value, // 切断処理のキュー ProtocolForTestQueueEnum::ALIVE->value // アライブチェック処理のキュー ]; public function getQueueList(): array { return (array)static::QUEUE_LIST; }
➤ステータスUNITリストの取得
以下のメソッドがフレームワーク内部でコールされる事によって、キューとUNITの紐づけが登録されます。【メソッド】getUnitList(string $p_que): array 【パラメータ】 $p_que - string - 必須 - キュー名 【戻り値】array - キューごとのUNIT集合のリスト(連想配列)
フレームワーク内部では
getQueueList
メソッドで取得したキュー名を元に、getUnitList
メソッドがコールされるため、引数にはキュー名が渡されます。例えば
SEND
キューとRECV
キューにそれぞれ2つずつUNITの集合を登録する場合、以下のように引数で与えられたキュー名に対応するリストを返す必要があります。public function getUnitList(string $p_que): array { $ret = []; // SENDキューのUNIT集合を登録 if($p_que === ProtocolForTestQueueEnum::SEND->value) { // STARTステータスとUNIT(getSendStart)の紐づけ $ret[] = [ 'status' => ProtocolForTestStatusEnum::START->value, 'unit' => $this->getSendStart() ]; // SEND_COMPLETEステータスとUNIT(getSendComplete)の紐づけ $ret[] = [ 'status' => ProtocolForTestStatusEnum::SEND_COMPLETE->value, 'unit' => $this->getSendComplete() ]; } // RECVキューのUNIT集合を登録 if($p_que === ProtocolForTestQueueEnum::RECV->value) { // STARTステータスとUNIT(getRecvStart)の紐づけ $ret[] = [ 'status' => ProtocolForTestStatusEnum::START->value, 'unit' => $this->getRecvStart() ]; // RECV_COMPLETEステータスとUNIT(getRecvComplete)の紐づけ $ret[] = [ 'status' => ProtocolForTestStatusEnum::RECV_COMPLETE->value, 'unit' => $this->getRecvComplete() ]; } . . . return $ret; }
➤ステータスUNITの実装
ここではgetUnitList
メソッド内で紐づけたUNITを定義します。【メソッド】<任意のメソッド名>(): Closure|string|null 【パラメータ】なし 【戻り値】 Closure|string - ステータスUNITの定義: Closure パラメータ: $p_param - SocketManagerParameter - 必須 - UNITパラメータクラスのインスタンス 各イベントハンドラで共通の引数として使用されるインスタンス。 SocketManagerParameterクラスを継承した拡張クラスを指定する事も可能。 戻り値: string|null - 遷移先がある場合: string 遷移先のキュー名 - 遷移先がない(終了する)場合: null - ステータスUNITの定義: string ヘルパー関数などの関数名
以下では
SEND
キューのUNIT集合とRECV
キューのUNIT集合をポーリングUNITを使って実装した例をご紹介します。ポーリングUNITの詳細については▶イベントハンドラについての説明をご覧ください。
- 【SENDキューのUNIT集合】
-
ここでは、スタックエリアの送信データをポーリングUNIT(getSendComplete)を使って送信する例をご紹介しています。
protected function getSendStart() { return function(ParameterForTest $p_param): ?string { // スタックエリアから送信データを取得する $send_data = $p_param->protocol()->getSendData(); // 送信データの設定 $p_param->protocol()->setSendingData($send_data); // SEND_COMPLETEステータス(getSendComplete)のUNITへ遷移 return ProtocolForTestStatusEnum::SEND_COMPLETE->value; }; } protected function getSendComplete() { return function(ParameterForTest $p_param): ?string { // データ送信 $w_ret = $p_param->protocol()->sending(); // nullの場合は送信中のためポーリングを続ける if($w_ret === null) { // 自身のステータス名を返してポーリングする $sta = $p_param->getStatusName(); return $sta; } // nullを返して終了する return null; }; }
- 【RECVキューのUNIT集合】
-
ここでは、仮に10バイトの固定データをポーリングUNIT(getRecvComplete)を使って受信する例をご紹介しています。
※実際には、実装する各プロトコルフォーマットに合わせてデータ長を取得する必要があります。
protected function getRecvStart() { return function(ParameterForTest $p_param): ?string { // 受信データサイズの設定 $p_param->protocol()->setReceivingSize(10); // RECV_COMPLETEステータス(getRecvComplete)のUNITへ遷移 return ProtocolForTestStatusEnum::RECV_COMPLETE->value; }; } protected function getRecvComplete() { return function(ParameterForTest $p_param): ?string { // データ受信 $w_ret = $p_param->protocol()->receiving(); // nullの場合は受信中のためポーリングを続ける if($w_ret === null) { // 自身のステータス名を返してポーリングする $sta = $p_param->getStatusName(); return $sta; } // 受信したデータを受信スタックへ設定 $p_param->setRecvStack($w_ret); // nullを返して終了する return null; }; }
getRecvComplete
メソッド(UNIT)内で$p_param->setRecvStack()
メソッドを使って受信データを設定する事で、コマンドディスパッチャーやコマンドUNITに受信データを引き渡す事ができます。
※送受信メソッドには▶UNITパラメータクラスのページでご紹介した
protocol()
のメソッドチェーンを使用しています。※UNITパラメータクラスには▶UNITパラメータクラスのページで使用した
ParameterForTest
を指定しています。おわりに
プロトコルUNITクラスはプロトコルの実装を担う部分なので、通信データとして扱うデータは基本的にバイナリデータである事に注意してください。
そのため、メソッドチェーンの仲介役である
生成されたクラスのインスタンスは、メイン処理クラス内で
複数のプロトコルUNITクラスやメイン処理クラスを用意している場合は、サーバーの実装内容によって最適なインスタンスを動的、あるいは静的に適用する事で柔軟なサーバー構築が可能になります。
そのため、メソッドチェーンの仲介役である
protocol()
を介して送受信を行う必要があります。生成されたクラスのインスタンスは、メイン処理クラス内で
$manager->setProtocolUnits()
メソッドに引き渡す事で適用されます。複数のプロトコルUNITクラスやメイン処理クラスを用意している場合は、サーバーの実装内容によって最適なインスタンスを動的、あるいは静的に適用する事で柔軟なサーバー構築が可能になります。