【TCP クライアントの実装】
はじめに
本記事では、SOCKET‑MANAGER Framework の SimpleSocket 機能を使った TCP クライアントの実装手順を分かりやすく解説します。
ジェネレータ(
ここでは専用のコマンドを使ってスキャフォールディング可能な TCP クライアントをメイン処理クラスとして実装する方法をご紹介します。
メイン処理クラスはアプリケーションのエントリポイントとして機能し、TCP クライアントのインスタンスはジェネレータクラスで生成されます。
また、CLI上で表示されるアプリケーション識別子や説明文、さらにコマンドライン引数も含めたメイン処理クラス上でのカスタマイズできる内容を中心にご紹介します。
メイン処理クラスを生成するコマンドは以下の通り。
生成されたクラスは
新しいメイン処理クラスが増えるとUsageの
上記Usageのように
ジェネレータ(
SimpleSocketGenerator)の初期設定、ノンブロッキングループの運用、ログ/UNIT連携、常時実行コールバックの実装例まで、実運用を見据えたコード例と運用ポイントを掲載します。ここでは専用のコマンドを使ってスキャフォールディング可能な TCP クライアントをメイン処理クラスとして実装する方法をご紹介します。
メイン処理クラスはアプリケーションのエントリポイントとして機能し、TCP クライアントのインスタンスはジェネレータクラスで生成されます。
また、CLI上で表示されるアプリケーション識別子や説明文、さらにコマンドライン引数も含めたメイン処理クラス上でのカスタマイズできる内容を中心にご紹介します。
メイン処理クラスを生成するコマンドは以下の通り。
> php worker simple:tcp-client MainForTest
[success] TCPクライアントのメイン処理クラス生成に成功しました (MainForTest)
生成されたクラスは
app/MainClassの場所にMainForTest.phpというファイル名で格納されます。新しいメイン処理クラスが増えるとUsageの
mainカテゴリに項目が追加されます。
> php worker
SOCKET-MANAGER Framework 1.X.X
Usage:
command [arguments]
main
app:main-for-test Command description
craft
craft:init <初期化クラス名> 初期化クラスの生成
craft:parameter <UNITパラメータクラス名> UNITパラメータクラスの生成
craft:protocol <プロトコルUNIT定義のクラス名> プロトコルUNIT定義のクラスとステータス名Enumの生成
craft:command <コマンドUNIT定義のクラス名> コマンドUNIT定義のクラスとキュー/ステータス名Enumの生成
craft:main <メイン処理のクラス名> メイン処理クラスの生成
craft:setting <設定ファイル名> 設定ファイルの生成
craft:locale <メッセージファイル名> メッセージファイルの生成
runtime
runtime:init <初期化クラス名> 初期化クラスの生成
runtime:parameter <UNITパラメータクラス名> UNITパラメータクラスの生成
runtime:units <ランタイムUNIT定義のクラス名> ランタイムUNIT定義のクラスとキュー/ステータス名Enumの生成
runtime:main <メイン処理のクラス名> メイン処理クラスの生成
simple
simple:tcp-server <メイン処理のクラス名> TCPサーバー用メイン処理クラスの生成
simple:tcp-client <メイン処理のクラス名> TCPクライアント用メイン処理クラスの生成
simple:udp <メイン処理のクラス名> UDP通信用メイン処理クラスの生成
上記Usageのように
mainカテゴリにapp:main-for-testという名前で追加されているのが確認できます。クライアント識別子
メイン処理クラスが生成されると以下のプロパティが確認できます。
ここではコマンドラインフォーマットを定義しています。
そしてクライアントアプリケーション名や、コマンドパラメータは自由にカスタマイズできます。
例えばコマンドパラメータにホスト名を追加したい場合は以下のようにします。
ちなみに"はてな(?)マーク"は最後尾の連続したパラメータにしか指定できません。
例えば
なお、メイン処理クラス内では
省略可能なパラメータが省略されている時はnullが返却されます。
/**
* @var string $identifer アプリケーション識別子
*/
protected string $identifer = 'app:main-for-test {port_no?}';
ここではコマンドラインフォーマットを定義しています。
app:main-for-testの部分がクライアントアプリケーション(プロセス)名で、{port_no?}の部分がコマンドパラメータ名であり、デフォルトで定義される内容です。そしてクライアントアプリケーション名や、コマンドパラメータは自由にカスタマイズできます。
例えばコマンドパラメータにホスト名を追加したい場合は以下のようにします。
/**
* @var string $identifer アプリケーション識別子
*/
protected string $identifer = 'app:main-for-test {host_name} {port_no?}';
port_noの後ろに付いている"はてな(?)マーク"は省略可能である事を表しています。ちなみに"はてな(?)マーク"は最後尾の連続したパラメータにしか指定できません。
例えば
{host_name?} {port_no?}と書く事はできますが{host_name?} {port_no}と書く事はできません。なお、メイン処理クラス内では
$this->getParameter('port_no')のように書く事でパラメータを取得できます。省略可能なパラメータが省略されている時はnullが返却されます。
コマンド説明文
メイン処理クラスが生成されると以下のプロパティが確認できます。
ここではUsageを表示した時のクライアントコマンドの説明文を定義しています。
初期状態では
例えば以下のように変更すると
Usageでは以下のように表示されます。
/**
* @var string $description コマンド説明
*/
protected string $description = 'Command description';
ここではUsageを表示した時のクライアントコマンドの説明文を定義しています。
初期状態では
Command descriptionとなっているので、これをプロパティで変更できます。例えば以下のように変更すると
/**
* @var string $description コマンド説明
*/
protected string $description = 'テスト用のクライアント';
Usageでは以下のように表示されます。
> php worker
SOCKET-MANAGER Framework 1.X.X
Usage:
command [arguments]
main
app:main-for-test テスト用のクライアント
craft
craft:init <初期化クラス名> 初期化クラスの生成
craft:parameter <UNITパラメータクラス名> UNITパラメータクラスの生成
craft:protocol <プロトコルUNIT定義のクラス名> プロトコルUNIT定義のクラスとステータス名Enumの生成
craft:command <コマンドUNIT定義のクラス名> コマンドUNIT定義のクラスとキュー/ステータス名Enumの生成
craft:main <メイン処理のクラス名> メイン処理クラスの生成
craft:setting <設定ファイル名> 設定ファイルの生成
craft:locale <メッセージファイル名> メッセージファイルの生成
runtime
runtime:init <初期化クラス名> 初期化クラスの生成
runtime:parameter <UNITパラメータクラス名> UNITパラメータクラスの生成
runtime:units <ランタイムUNIT定義のクラス名> ランタイムUNIT定義のクラスとキュー/ステータス名Enumの生成
runtime:main <メイン処理のクラス名> メイン処理クラスの生成
simple
simple:tcp-server <メイン処理のクラス名> TCPサーバー用メイン処理クラスの生成
simple:tcp-client <メイン処理のクラス名> TCPクライアント用メイン処理クラスの生成
simple:udp <メイン処理のクラス名> UDP通信用メイン処理クラスの生成
設定可能項目
このメイン処理クラスであらかじめ設定可能な項目は、
各引数の設定方法には以下の3種類があります。
この SimpleSocketGenerator クラスの引数の内訳は次の通りです。
御覧の通り、直接値を指定する事もできますが、引数を省略したり、nullを指定する事で自動的にデフォルト値が適用される仕組みになっています。
この
御覧の通り、直接値を指定する事もできますが、引数を省略しても自動的にデフォルト値が適用される仕組みになっています。
なお、ここでは SimpleSocketGenerator クラスの引数のようにnullを指定する事はできません。
SimpleSocketGenerator クラスのコンストラクタ引数です。各引数の設定方法には以下の3種類があります。
- ①メイン処理クラス内で直接指定
- この項目でご紹介する方法です。
- ②コマンドラインから取得
- 上記の>> クライアント識別子の項目でご紹介した方法です。
- ③設定ファイル
- ▶設定ファイルのページでご紹介している方法です。
SimpleSocketGeneratorクラスの引数
メイン処理クラスの実行(exec)メソッドの冒頭部分には SimpleSocketGenerator クラスのインスタンス生成部分である以下のコードが含まれています。
public function exec()
{
// 引数の取得
$port_no = $this->getParameter('port_no');
/***********************************************************************
* シンプルソケットジェネレータの初期設定
*
* ジェネレータインスタンスの生成や各種設定をここで実行します
**********************************************************************/
$generator = new SimpleSocketGenerator(SimpleSocketTypeEnum::TCP_CLIENT, 'localhost', $port_no);
・
・
・
}
この SimpleSocketGenerator クラスの引数の内訳は次の通りです。
- シンプルソケットタイプ(今回の場合は SimpleSocketTypeEnum.TCP_CLIENT)
- ホスト名(デフォルト:127.0.0.1)
- ポート番号(デフォルト:15000)
- ダウンタイム(デフォルト:100ms)
- 送受信バッファサイズ(デフォルト:255)
- バッファスタック件数(デフォルト:1)
- 言語コード(デフォルト:'ja')
- 接続制限数(デフォルト:10)
- 接続リトライ回数(デフォルト:3)
- リトライ時インターバル(デフォルト:1000μs)
$generator = new SimpleSocketGenerator( SimpleSocketTypeEnum::TCP_CLIENT, '127.0.0.1', 15000, 100, 255, 1, 'ja', 10, 3, 1000 );
$generator = new SimpleSocketGenerator( SimpleSocketTypeEnum::TCP_CLIENT );
$generator = new SimpleSocketGenerator( SimpleSocketTypeEnum::TCP_CLIENT, null, null, null, null, null, null, null, null, null );
御覧の通り、直接値を指定する事もできますが、引数を省略したり、nullを指定する事で自動的にデフォルト値が適用される仕組みになっています。
cycleDrivenメソッドの引数
メイン処理クラスの実行(exec)メソッド末尾のソースにはノンブロッキングループ内でcycleDrivenメソッドが呼ばれている箇所があります。
public function exec()
{
・
・
・
// ノンブロッキングループ
while(true)
{
// 周期ドリブン
$ret = $generator->cycleDriven();
if($ret === false)
{
goto finish;
}
}
・
・
・
}
この
cycleDrivenメソッドの引数の内訳は次の通りです。- 周期インターバルタイム(デフォルト:2000μs)
$generator->cycleDriven( 2000 );
$generator->cycleDriven();
御覧の通り、直接値を指定する事もできますが、引数を省略しても自動的にデフォルト値が適用される仕組みになっています。
なお、ここでは SimpleSocketGenerator クラスの引数のようにnullを指定する事はできません。
ログライターの登録
メイン処理クラスの実行(exec)メソッドの中ほどに以下の定義があります。
function 内に処理を定義する事もできますが、SocketManager などであらかじめ作成しておいた初期化クラスを使って登録する事もできます。
public function exec()
{
・
・
・
/**
* ログライターの登録(任意)
*
* ログライターが使いたい場合に$generator->setLogWriter()メソッドで登録します
* SocketManager初期化クラスのログライターをそのままお使い頂けます
*/
$generator->setLogWriter
(
/**
* ログライター
*
* @param string $p_level ログレベル(debug、info、notice、warning、errorなど)
* @param array $p_param 連想配列形式のログ内容
* @return void
*/
function(string $p_level, array $p_param): void
{
}
);
・
・
・
}
function 内に処理を定義する事もできますが、SocketManager などであらかじめ作成しておいた初期化クラスを使って登録する事もできます。
public function exec()
{
・
・
・
// 初期化クラスのインスタンスを生成
$init = new InitForTest();
・
・
・
/**
* ログライターの登録(任意)
*
* ログライターが使いたい場合に$generator->setLogWriter()メソッドで登録します
* SocketManager初期化クラスのログライターをそのままお使い頂けます
*/
$generator->setLogWriter( $init->getLogWriter() );
・
・
・
}
SocketManager との連携
メイン処理クラスの実行(exec)メソッドの中ほどに以下のコメント部分があります。
あらかじめ作成しておいたSocketManagerなどの UNIT パラメータクラスのインスタンスを、ここで以下のように引き渡して UNIT 処理と連携できます。
ここで設定した UNIT パラメータインスタンスを使って、UNIT 処理やコマンドディスパッチャーで利用できるようになります。
public function exec()
{
・
・
・
/**
* SocketManagerとの連携(任意)
*
* UNITパラメータインスタンスの"simple_socket"プロパティにシンプルソケットインスタンスが設定され
* コマンドディスパッチャーやステータスUNIT内で使えるようになります
*
* $generator->setUnitParameter()メソッドでUNITパラメータクラスを設定します
*/
・
・
・
}
あらかじめ作成しておいたSocketManagerなどの UNIT パラメータクラスのインスタンスを、ここで以下のように引き渡して UNIT 処理と連携できます。
public function exec()
{
・
・
・
// UNITパラメータのインスタンスを生成
$param = new ParameterForTest();
・
・
・
/**
* SocketManagerとの連携(任意)
*
* UNITパラメータインスタンスの"simple_socket"プロパティにシンプルソケットインスタンスが設定され
* コマンドディスパッチャーやステータスUNIT内で使えるようになります
*/
$generator->setUnitParameter( $param );
・
・
・
}
ここで設定した UNIT パラメータインスタンスを使って、UNIT 処理やコマンドディスパッチャーで利用できるようになります。
常時実行コールバックの登録
メイン処理クラスの実行(exec)メソッドの中ほどに以下の定義があります。
以下のように function 内に処理を定義する事もできますが、ヘルパー関数などの関数名を指定する事もできます。
public function exec()
{
・
・
・
/**
* 常時実行処理の登録(任意)
*
* 常時実行処理がある場合に$generator->setKeepRunning()メソッドで登録します
*/
$generator->setKeepRunning
(
/**
* 常時実行処理
*
* @param ISimpleSocketTcpClient $p_simple_socket シンプルソケットインスタンス
* @param mixed[] $p_argv 可変引数(setKeepRunningメソッドの第二引数以降のものが渡される)
* @return void
*/
function(ISimpleSocketTcpClient $p_simple_socket): void
{
}
);
・
・
・
}
以下のように function 内に処理を定義する事もできますが、ヘルパー関数などの関数名を指定する事もできます。
public function exec()
{
・
・
・
/**
* 常時実行処理の登録(任意)
*
* 常時実行処理がある場合に$generator->setKeepRunning()メソッドで登録します
*/
$generator->setKeepRunning
(
/**
* 常時実行処理
*
* @param ISimpleSocketTcpClient $p_simple_socket シンプルソケットインスタンス
* @param mixed[] $p_argv 可変引数(setKeepRunningメソッドの第二引数以降のものが渡される)
* @return void
*/
function(ISimpleSocketTcpClient $p_simple_socket): void
{
// データ送信
$p_simple_socket->send('test data');
// データ受信
$data = $p_simple_socket->recv();
// データのログ出力
$p_simple_socket->logWriter('debug', ['受信データ' => $data]);
}
);
・
・
・
}
コアインスタンスの生成
メイン処理クラスの実行(exec)メソッドの中ほどに以下の定義があります。
シンプルソケットの各インターフェース用コアインスタンスが、ここで初めて生成されると同時に有効になります。
generate メソッド実行時の戻り値(ここでは
例えば初回起動時にデータ送信する場合、以下のように書く事ができます。
public function exec()
{
・
・
・
/**
* シンプルソケットインスタンスの生成
*
* この手続きが行われた時点でインスタンスが生成され有効になります
*/
$w_ret = $generator->generate();
if($w_ret === null)
{
goto finish;
}
・
・
・
}
シンプルソケットの各インターフェース用コアインスタンスが、ここで初めて生成されると同時に有効になります。
generate メソッド実行時の戻り値(ここでは
$w_ret 変数)には、setKeepRunning メソッドで設定するクロージャ、または関数の引数で渡される ISimpleSocketTcpClient インターフェースと同じものが設定されます(インスタンスの使い方は同じです)。例えば初回起動時にデータ送信する場合、以下のように書く事ができます。
$interface = $generator->generate();
if($interface === null)
{
goto finish;
}
$interface->send('test data');
おわりに
これまで見てきたように、メイン処理クラスではコマンドライン/設定項目/ログライター/SocketManager連携/常時実行コールバックを自由にデザインすることができます。
シンプルソケット機能単体でも IPC(サーバー間通信)として機能しますが、SocketManager と連携させる事で IPC として利用する事もできます。
シンプルソケット機能単体でも IPC(サーバー間通信)として機能しますが、SocketManager と連携させる事で IPC として利用する事もできます。