-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcommand-unit.html
More file actions
480 lines (385 loc) · 26.2 KB
/
command-unit.html
File metadata and controls
480 lines (385 loc) · 26.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>コマンドUNITクラスの実装 | SOCKET-MANAGER Framework For PHP</title>
<meta name="description" content="SOCKET-MANAGERフレームワークでのコマンドUNIT実装方法を解説。チャットサーバーやプライベートメッセージの実装例、イベントハンドラの定義方法、キューとステータスUNITの活用方法を具体的なコード例と共に紹介。" />
<meta content="PHP,ソケット通信,サーバー開発,framework,IPC,ソケットマネージャー" name="keywords">
<link rel="canonical" href="https://socket-manager.github.io/document/command-unit.html" />
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LF9W695NNW"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-LF9W695NNW');
</script>
<link rel="icon" href="https://socket-manager.github.io/document/favicon.ico" type="image/x-icon" />
<link type="text/css" rel="stylesheet" href="./css/common.css" media="all" />
<script src="./js/jquery-3.7.1.min.js"></script>
<script type="text/javascript" src="./js/common.js"></script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "TechArticle",
"headline": "SOCKET-MANAGER Framework - コマンドUNITクラスの実装ガイド",
"description": "SOCKET-MANAGERフレームワークにおけるコマンドUNITクラスの実装方法を解説。チャットサーバーやプライベートメッセージの実装例、イベントハンドラの定義方法、キューとステータスUNITの活用方法を具体的なコード例と共に紹介。",
"keywords": "PHP, ソケット通信, コマンド実装, 非同期処理, ソケットマネージャー",
"articleSection": ["Technical Documentation", "Server Development", "PHP Programming"],
"author": {
"@type": "Person",
"name": "SOCKET-MANAGER開発チーム"
},
"publisher": {
"@type": "Organization",
"name": "SOCKET-MANAGER",
"logo": {
"@type": "ImageObject",
"url": "https://socket-manager.github.io/document/logo.png",
"width": 355,
"height": 50
}
},
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://socket-manager.github.io/document/command-unit.html"
},
"url": "https://socket-manager.github.io/document/command-unit.html",
"breadcrumb": {
"@type": "BreadcrumbList",
"itemListElement": [{
"@type": "ListItem",
"position": 1,
"name": "Framework Top",
"item": "https://socket-manager.github.io/document/"
},{
"@type": "ListItem",
"position": 2,
"name": "コマンドUNITクラスの実装",
"item": "https://socket-manager.github.io/document/command-unit.html"
}]
},
"isPartOf": {
"@type": "WebSite",
"name": "フレームワークのご紹介",
"url": "https://socket-manager.github.io/document/"
}
}
</script>
</head>
<body>
<div class="layout">
<div class="menu" role="navigation" aria-label="ページメニュー">
<h2 class="menu-title">SOCKET-MANAGER</h2>
<h4 class="menu-reference menu-page-title-bottom"><a href="./reference/" target="_blank">>> Reference</a></h4>
<h2 class="menu-label">MAIN-MENU</h2>
<div class="menu-text">
<h3 class="menu-page-title-link"><a href="./">▶フレームワークのご紹介</a></h3>
<h3 class="menu-page-title-link"><a href="./event-handler.html">▶イベントハンドラについて</a></h3>
</div>
<h3 class="menu-label-sub">IMPLEMENT</h3>
<div class="menu-text">
<h3 class="menu-page-title-link"><a href="./init-class.html">▶初期化クラス</a></h3>
<h3 class="menu-page-title-link"><a href="./unit-parameter.html">▶UNITパラメータクラス</a></h3>
<h3 class="menu-page-title-link"><a href="./protocol-unit.html">▶プロトコルUNITクラス</a></h3>
<h3 class="menu-page-title">▼コマンドUNITクラス</h3>
<ul>
<li><a href="./command-unit.html#begin">はじめに</a></li>
</ul>
<ul>
<li><a href="./command-unit.html#enum-queue">キュー名の定義</a></li>
</ul>
<ul>
<li><a href="./command-unit.html#enum-status">ステータス名の定義</a></li>
</ul>
<ul>
<li><a href="./command-unit.html#implement">コマンドUNITクラスの実装</a></li>
</ul>
<ul>
<li><a href="./command-unit.html#last">おわりに</a></li>
</ul>
<h3 class="menu-page-title-link"><a href="./main.html">▶メイン処理クラス</a></h3>
<h3 class="menu-page-title-link"><a href="./setting.html">▶設定ファイル</a></h3>
<h3 class="menu-page-title-link"><a href="./message.html">▶メッセージファイル</a></h3>
</div>
<div class="menu-line"></div>
<div class="menu-text">
<h3 class="menu-page-title-link-for-runtime-manager"><a href="./runtime-manager/" target="_blank">>> ランタイムライブラリ</a></h3>
<h3 class="menu-page-title-link-for-runtime-manager"><a href="./simple-socket/" target="_blank">>> シンプルソケット機能</a></h3>
</div>
<h3 class="menu-label-sub">ADVANCED</h3>
<div class="menu-text">
<h3 class="menu-page-title-link"><a href="./architecture.html">▶アーキテクチャ</a></h3>
<h3 class="menu-page-title-link"><a href="./event.html">▶イベント駆動アーキテクチャ</a></h3>
<h3 class="menu-page-title-link"><a href="./ipc.html">▶IPC(プロセス間通信)</a></h3>
<h3 class="menu-page-title-link"><a href="./multi-server.html">▶マルチサーバーの構成</a></h3>
<h3 class="menu-page-title-link"><a href="./tcp-and-udp.html">▶TCP/UDP通信について</a></h3>
<h3 class="menu-page-title-link"><a href="./laravel.html">▶Laravelと連携する</a></h3>
<h3 class="menu-page-title-link"><a href="./system-setting.html">▶システム設定ファイル</a></h3>
<h3 class="menu-page-title-link"><a href="./custom-command.html">▶カスタムコマンド作成機能</a></h3>
</div>
<h3 class="menu-label-sub">OTHER-PROJECT</h3>
<div class="menu-text">
<h3 class="menu-page-title-link"><a href="./new-project.html">▶新規開発環境</a></h3>
<h3 class="menu-page-title-link"><a href="./websocket.html">▶Websocketサーバー開発環境</a></h3>
<h3 class="menu-page-title-link"><a href="./dev-ops.html">▶フレームワークのDevOps環境</a></h3>
</div>
<div class="menu-line"></div>
<div class="menu-text">
<h3 class="menu-page-title-link-for-minecraft"><a href="./minecraft-contents/" target="_blank">>> マインクラフト専用環境</a></h3>
<h3 class="menu-page-title-link-for-launcher"><a href="./launcher/" target="_blank">>> GUI & CLI ランチャー</a></h3>
<h3 class="menu-page-title-link-for-rest-api"><a href="./rest-api/" target="_blank">>> REST-APIサーバー開発環境</a></h3>
</div>
<h2 class="menu-label">EXTRA-MENU</h2>
<div class="menu-text">
<h3 class="menu-page-title-link"><a href="./extra-demo.html">▶デモサーバーの種類</a></h3>
<h3 class="menu-page-title-link"><a href="./extra-demo-command.html">▶デモのコマンド仕様</a></h3>
<h3 class="menu-page-title-link"><a href="./extra-demo-setting.html">▶デモの設定ファイル</a></h3>
<h3 class="menu-page-title-link"><a href="./extra-minecraft.html">▶マインクラフトの通信仕様</a></h3>
<h3 class="menu-page-title-link"><a href="./extra-close-frame.html">▶切断フレームの検証</a></h3>
</div>
<h2 class="menu-label">PHP-TECHNIQUE</h2>
<div class="menu-text">
<h3 class="menu-page-title-link"><a href="./php-pass-by-reference.html">▶参照渡し</a></h3>
<h3 class="menu-page-title-link"><a href="./php-phpdoc.html">▶PHPDocのフォーマット</a></h3>
</div>
<div class="menu-dummy-for-framework"></div>
</div>
<div class="main" role="main">
<h1>【コマンドUNITクラスの実装】</h1>
<a id="begin"></a>
<h2 class="subtitle">はじめに</h2>
<div class="text-block">
本フレームワークでは、アプリケーション層の処理を担うビジネスロジック部を、通信プロトコル層から分離された独立モジュールとして構成しています。<br />
これにより、プロトコルごとの仕様に依存することなく、用途や役割に応じたロジッククラスを柔軟に定義・差し替えることが可能です。<br /><br />
また、プロトコル部と同様にスキャフォールディング(コード自動生成)機能にも対応しており、共通のイベントループ上で動作するロジック部をテンプレートベースで迅速に構築できます。<br />
これらの特長により、高い再利用性と保守性を確保しつつ、開発の生産性を飛躍的に向上させることが可能です。<br /><br />
このクラスでは、クライアントからのリクエストやサーバーの通知から始まる一連の流れを1つのコマンド処理とみなし、それをサーバーコンテンツを構成する1つの要素としてイベントハンドラを構成していきます。<br /><br />
コマンドUNITクラスを生成するコマンドは以下の通り。<br />
<pre color-change="command" aria-label="コマンド実行(CommandForTestというクラス名を指定する場合)">
> php worker craft:command CommandForTest
[success] コマンドUNITクラスの生成に成功しました (CommandForTest)
[success] コマンドUNITのキュー名Enumの生成に成功しました (CommandForTestQueueEnum)
[success] コマンドUNITのステータス名Enumの生成に成功しました (CommandForTestStatusEnum)
</pre><br />
コマンドを実行する事で<code>app/CommandUnits</code>の場所に以下3つのファイルが生成されます。<br />
<dl>
<dt>・CommandForTest</dt>
<dd>
<code>IEntryUnits</code>インターフェイスがimplementsされたコマンドUNITクラスです。<br />
ここにコマンド処理のイベントハンドラを実装します。<br />
</dd>
<dt>・CommandForTestQueueEnum</dt>
<dd>
ここにコマンド処理のキュー(イベント)名を定義します。<br />
</dd>
<dt>・CommandForTestStatusEnum</dt>
<dd>
ここにコマンド処理のステータス(UNIT)名を定義します。<br />
</dd>
</dl>
このフレームワーク環境では、キュー名とステータス名をEnumで定義する事を推奨しているので、それぞれのEnumファイルに分けて出力されます。<br /><br />
以降では生成されたファイルの内容を見ていきます。<br />
</div><br />
<a id="enum-queue"></a>
<h2 class="subtitle">キュー名の定義</h2>
<div class="text-block">
<code>CommandForTestQueueEnum.php</code>のファイルは、イベントに対応するキュー名を定義するEnumファイルです。<br />
これらのキュー名はコマンドUNITクラスで定義されるイベントごとのUNITの集合を特定するためのものです。<br />
今回のケースでは<code>CommandForTest</code>クラス内でキューとUNITの紐づけを行います。<br />
紐づけの方法は以下の<font><a href="./command-unit.html#implement">>> コマンドUNITクラスの実装</a></font>の項で説明しています。<br />
</div><br />
<a id="enum-status"></a>
<h2 class="subtitle">ステータス名の定義</h2>
<div class="text-block">
<code>CommandForTestStatusEnum.php</code>のファイルは、コマンドUNITのステータス名を定義するEnumファイルです。<br />
コマンドUNITのステータス名であらかじめ予約されているものは<code>StatusEnum::START</code>だけで、生成されたEnumファイルにはその内容が代入されています。<br />
それ以外のステータス名は自由に定義する事ができます。<br />
定義済みのステータス名は以下の通り。<br />
<dl>
<dt>CommandForTestStatusEnum::START</dt>
<dd>
同じキューに登録されているUNITの集合のうち一番最初に実行されるステータスです。<br />
UNITは必ずSTARTステータスから始まるルールになっています。<br />
</dd>
</dl>
これらのステータス名はコマンドUNITクラスで定義したUNITに紐づくものなので、今回のケースでは先ほど生成した<code>CommandForTest</code>クラス内でUNITとステータス名の紐づけを行います。<br />
紐づけの方法は以下の<font><a href="./command-unit.html#implement">>> コマンドUNITクラスの実装</a></font>の項で説明しています。<br /><br />
※ここで定義したステータス名は異なるキューで再利用が可能です。<br />
</div><br />
<a id="implement"></a>
<h2 class="subtitle">コマンドUNITクラスの実装</h2>
<div class="text-block">
<code>CommandForTest.php</code>のファイルにはUNIT定義を含めた各種メソッドが実装されています。<br />
各メソッドの仕様と実装例は以下の通り。<br />
<br />
<h3 class="underline">➤キューリストの取得</h3>
フレームワークは以下のメソッドをコールして必要なキューのリストを登録します。<br />
<pre aria-label="シグネチャ">
【メソッド】getQueueList(): array
【パラメータ】なし
【戻り値】array - キュー名のリスト
</pre><br />
例えば<font><a href="./event-handler.html" target="_blank">▶イベントハンドラについて</a></font>のページでご紹介したチャットサーバーを例に挙げると、以下のように実装します。<br />
<pre color-change="php" aria-label="メソッドの実装例">
protected const QUEUE_LIST = [
CommandForTestQueueEnum::CHAT_MESSAGE->value, // チャットメッセージを処理するキュー
CommandForTestQueueEnum::PRIVATE_MESSAGE->value // プライベートメッセージを処理するキュー
];
public function getQueueList(): array
{
return (array)static::QUEUE_LIST;
}
</pre><br />
<br />
<h3 class="underline">➤ステータスUNITリストの取得</h3>
以下のメソッドがフレームワーク内部でコールされる事によって、キューとUNITの紐づけが登録されます。<br />
<pre aria-label="シグネチャ">
【メソッド】getUnitList(string $p_que): array
【パラメータ】
$p_que - string - 必須 - キュー名
【戻り値】array - キューごとのUNIT集合のリスト(連想配列)
</pre><br />
フレームワーク内部では<code>getQueueList</code>メソッドで取得したキュー名を元に、<code>getUnitList</code>メソッドがコールされるため、引数にはキュー名が渡されます。<br />
例えば<code>CHAT_MESSAGE</code>キューと<code>PRIVATE_MESSAGE</code>キューにそれぞれ2つずつUNITの集合を登録する場合、以下のように引数で与えられたキュー名に対応するリストを返す必要があります。<br />
<pre color-change="php" aria-label="メソッドの実装例">
public function getUnitList(string $p_que): array
{
$ret = [];
// CHAT_MESSAGEキューのUNIT集合を登録
if($p_que === CommandForTestQueueEnum::CHAT_MESSAGE->value)
{
// STARTステータスとUNIT(getLogWrite)の紐づけ
$ret[] = [
'status' => CommandForTestStatusEnum::START->value,
'unit' => $this->getLogWrite()
];
// SENDステータスとUNIT(getChatSend)の紐づけ
$ret[] = [
'status' => CommandForTestStatusEnum::SEND->value,
'unit' => $this->getChatSend()
];
}
// PRIVATE_MESSAGEキューのUNIT集合を登録
if($p_que === CommandForTestQueueEnum::PRIVATE_MESSAGE->value)
{
// STARTステータスとUNIT(getLogWrite)の紐づけ
$ret[] = [
'status' => CommandForTestStatusEnum::START->value,
'unit' => $this->getLogWrite()
];
// SENDステータスとUNIT(getPrivateSend)の紐づけ
$ret[] = [
'status' => CommandForTestStatusEnum::SEND->value,
'unit' => $this->getPrivateSend()
];
}
.
.
.
return $ret;
}
</pre><br />
※STARTステータスで紐づけを行っているUNIT(getLogWrite)は再利用UNITとして登録しているので内容は同じものです。<br /><br />
<br />
<h3 class="underline">➤ステータスUNITの実装</h3>
ここでは<code>getUnitList</code>メソッド内で紐づけたUNITを定義します。<br />
<pre aria-label="シグネチャ">
【メソッド】<任意のメソッド名>(): Closure|string|null
【パラメータ】なし
【戻り値】
Closure|string
- ステータスUNITの定義: Closure
パラメータ:
$p_param - SocketManagerParameter - 必須 - UNITパラメータクラスのインスタンス
各イベントハンドラで共通の引数として使用されるインスタンス。
SocketManagerParameterクラスを継承した拡張クラスを指定する事も可能。
戻り値: string|null
- 遷移先がある場合: string
遷移先のキュー名
- 遷移先がない(終了する)場合: null
- ステータスUNITの定義: string
ヘルパー関数などの関数名
</pre><br />
以下では<code>CHAT_MESSAGE</code>キューのUNIT集合と<code>PRIVATE_MESSAGE</code>キューのUNIT集合を再利用UNITを使って実装した例をご紹介します。<br />
再利用UNITの概要については<font><a href="./event-handler.html" target="_blank">▶イベントハンドラについて</a></font>の説明をご覧ください。<br /><br />
<pre aria-label="受信するメッセージのJSON形式(実装例)">
【チャットメッセージ】
{ "message" => <メッセージ> }
【プライベートメッセージ】
{ "message" => <メッセージ>, "cid" => <相手先クライアントの接続ID> }
</pre><br />
<dl>
<dt>【CHAT_MESSAGEキューのUNIT集合】</dt>
<dd>
ここでは、受信したデータをディスクリプタ(クライアント接続子)へ保存し、メッセージをログファイルへ記録する再利用UNITを経由してから自身を除く全員へ配信する例をご紹介しています。<br />
<pre color-change="php" aria-label="CHAT_MESSAGEキューのUNIT集合の実装例">
protected function getLogWrite()
{
return function(ParameterForTest $p_param): ?string
{
// 現在のキューを取得
$que = $p_param->getQueueName();
// 受信データを取得
$recv_data = $p_param->getRecvData();
// メッセージをディスクリプタへ保存
$p_param->setTempBuff(['message_data' => $recv_data]);
// 自身のキューがCHAT_MESSAGEかどうかを判定してからログファイルへ記録
$key = 'private_message'; // プライベートメッセージ
if($que === CommandForTestQueueEnum::CHAT_MESSAGE->value)
{
$key = 'chat_message'; // チャットメッセージ
}
$p_param->logWriter('info', [$key => $recv_data['message']]);
// SENDステータス(getChatSend or getPrivateSend)のUNITへ遷移
return CommandForTestStatusEnum::SEND->value;
};
}
protected function getChatSend()
{
return function(ParameterForTest $p_param): ?string
{
// ディスクリプタからメッセージを取得
$message_data = $p_param->getTempBuff(['message_data']);
// 自身を除く全員へメッセージを配信
$p_param->setSendStackAll($message_data['message'], true);
// nullを返して終了する
return null;
};
}
</pre><br /><br />
</dd>
<dt>【PRIVATE_MESSAGEキューのUNIT集合】</dt>
<dd>
ここでは<code>CHAT_MESSAGE</code>キューに登録されている再利用UNIT(getLogWrite)と同じものを使うので、プライベートメッセージを送信するUNITだけを実装しています。<br />
<pre color-change="php" aria-label="PRIVATE_MESSAGEキューのUNIT集合の実装例">
protected function getPrivateSend()
{
return function(ParameterForTest $p_param): ?string
{
// ディスクリプタからメッセージを取得
$message_data = $p_param->getTempBuff(['message_data']);
// 宛先を指定してメッセージを配信
$p_param->setSendStack($message_data['message'], $message_data['cid']);
// nullを返して終了する
return null;
};
}
</pre><br />
</dd>
</dl><br />
今回使用した<code>$p_param->getTempBuff()</code>や<code>$p_param->setTempBuff()</code>のメソッドはディスクリプタ(クライアント接続子)へ対する読み込み/書き込みを行います。<br />
つまりUNITを跨ったグローバルデータではあるものの、各クライアント個別のデータとして利用できます。<br /><br />
メソッド(UNIT)内で使用している<code>$p_param->setSendStack()</code>や<code>$p_param->setSendStackAll()</code>を使って送信データを設定すると、送信スタックエリアを通じてプロトコルUNITに引き渡され、自動で送信されます。<br /><br />
※UNITパラメータクラスには<font><a href="./unit-parameter.html" target="_blank">▶UNITパラメータクラス</a></font>のページで使用した<code>ParameterForTest</code>を指定しています。<br />
</div><br />
<a id="last"></a>
<h2 class="subtitle">おわりに</h2>
<div class="text-block">
生成されたクラスのインスタンスは、メイン処理クラス内で<code>$manager->setCommandUnits()</code>メソッドに引き渡す事で適用されます。<br />
複数のコマンドUNITクラスやメイン処理クラスを用意している場合は、サーバーの実装内容によって最適なインスタンスを動的、あるいは静的に適用する事で柔軟なサーバー構築が可能になります。<br />
</div>
</div>
</div>
</body>
</html>