はじめに
本フレームワークでは、通信プロトコル層を抽象化したインターフェースを採用しており、TCPやUDPなどのトランスポートプロトコルを柔軟に切り替えられる構成になっています。
これにより、開発時や運用時に用途に応じたプロトコル選択が可能です。
さらに、プロトコル処理のスキャフォールディング(コード自動生成)機能も備えており、テンプレートベースでの開発を通じて、高い拡張性と保守性を確保しながら、実装作業の効率化を実現します。
プロトコル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)名を定義します。
このフレームワーク環境では、キュー名とステータス名をEnumで定義する事を推奨しているので、それぞれのEnumファイルに分けて出力されます。
以降では生成されたファイルの内容を見ていきます。
キュー名の定義
ProtocolForTestQueueEnum.php
のファイルは、イベントに対応するキュー名を定義するEnumファイルです。
プロトコルUNITのキュー名はあらかじめProtocolQueueEnum
で予約されていますので、生成されたEnumファイルにはその内容が代入されています。
定義済みのキュー名は以下の通り。
- ProtocolForTestQueueEnum::ACCEPT
- クライアントからの接続要求に対するアクセプト時に呼ばれる
- ProtocolForTestQueueEnum::CONNECT
- 当該サーバーから他のエンドポイントへ接続する時に呼ばれる
- ProtocolForTestQueueEnum::RECV
- 通信データを受信する時に呼ばれる
- ProtocolForTestQueueEnum::SEND
- 通信データを送信する時に呼ばれる
- ProtocolForTestQueueEnum::CLOSE
- 切断シーケンスを走らせる時に呼ばれる
- ProtocolForTestQueueEnum::ALIVE
- アライブチェックを走らせる時に呼ばれる
これらのキュー名はプロトコルUNITクラスで定義されるイベントごとのUNITの集合を特定するためのものです。
今回のケースではProtocolForTest
クラス内でキューとUNITの紐づけを行います。
紐づけの方法は以下の>> プロトコルUNITクラスの実装の項で説明しています。
必ずしも全てのキューを使う必要はありませんが、オリジナルプロトコルを開発する場合は少なくともSENDキューとRECVキューが必要になります。
ステータス名の定義
ProtocolForTestStatusEnum.php
のファイルは、プロトコルUNITのステータス名を定義するEnumファイルです。
プロトコルUNITのステータス名であらかじめ予約されているものはStatusEnum::START
だけで、生成されたEnumファイルにはその内容が代入されています。
それ以外のステータス名は自由に定義する事ができます。
定義済みのステータス名は以下の通り。
- ProtocolForTestStatusEnum::START
-
同じキューに登録されているUNITの集合のうち一番最初に実行されるステータスです。
UNITは必ずSTARTステータスから始まるルールになっています。
これらのステータス名はプロトコルUNITクラスで定義したUNITに紐づくものなので、今回のケースでは先ほど生成した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)を使って送信する例をご紹介しています。
SENDキューのUNIT集合の実装例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)を使って受信する例をご紹介しています。
(実際には、実装する各プロトコルフォーマットに合わせてデータ長を取得する必要があります)RECVキューのUNIT集合の実装例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に受信データを引き渡す事ができます。
送受信メソッドには以下のページでご紹介したprotocol()のメソッドチェーンを使用しています。
UNITパラメータクラスには以下のページで使用したParameterForTestを指定しています。
おわりに
プロトコルUNITクラスはプロトコルの実装を担う部分なので、通信データとして扱うデータは基本的にバイナリデータである事に注意してください。
そのため、メソッドチェーンの仲介役であるprotocol()
を介して送受信を行う必要があります。
生成されたクラスのインスタンスは、メイン処理クラス内で$manager->setProtocolUnits()
メソッドに引き渡す事で適用されます。
複数のプロトコルUNITクラスやメイン処理クラスを用意している場合は、サーバーの実装内容によって最適なインスタンスを動的、あるいは静的に適用する事で柔軟なサーバー構築が可能になります。