0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SOCKET-MANAGERを使った自作サーバー開発の手引き<プロトコルUNITクラスの実装>

Last updated at Posted at 2025-06-22

はじめに

本フレームワークでは、通信プロトコル層を抽象化したインターフェースを採用しており、TCPやUDPなどのトランスポートプロトコルを柔軟に切り替えられる構成になっています。
これにより、開発時や運用時に用途に応じたプロトコル選択が可能です。

さらに、プロトコル処理のスキャフォールディング(コード自動生成)機能も備えており、テンプレートベースでの開発を通じて、高い拡張性と保守性を確保しながら、実装作業の効率化を実現します。

プロトコルUNITクラスを生成するコマンドは以下の通り。

コマンド実行(ProtocolForTestというクラス名を指定する場合)
> 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クラスやメイン処理クラスを用意している場合は、サーバーの実装内容によって最適なインスタンスを動的、あるいは静的に適用する事で柔軟なサーバー構築が可能になります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?