- think-swoole擴展的websocket均可為監聽事件!!!
- 創建一個監聽類,在項目根目錄下執行:
~~~
php think make:listener WebSocketEvent
~~~
在`swoole.php`的`websocket['subscribe']`寫入監聽
~~~
'subscribe' => [
app\listener\WebSocketEvent::class
],
~~~
- `listen`和`subscribe`是一個東西,一個以文件為單位,一個以組為單位.subscribe的方法必須以`on`開頭詳見官方文檔.
- 特別說明:`onOpen`觸發的事件名為`connect`并非`open`.
- 其它詳細細則,寫在`WebsocketEvent.php`結合代碼說明.
<details>
<summary>WebsocketEvent.php</summary>
~~~
<?php
declare (strict_types=1);
namespace app\listener;
use app\Request;
use Swoole\Server;
use think\Container;
use think\swoole\Table;
use think\swoole\Websocket;
class WebSocketEvent
{
protected $websocket = null;
protected $server = null;
protected $table = null;
protected $u2fd = null;
protected $fd2u = null;
/**
* WebSocketEvent constructor.
* @param Server $server server對象
* @param Websocket $websocket websocket對線
* @param Container $container 容器
*/
public function __construct(Server $server, Websocket $websocket, Container $container)
{
//這里取對象的方式有很多種,看個人習慣,在此列舉其中幾種方式
$this->websocket = $websocket;//依賴注入的方式
$this->server = $server;
//think\table對象
$this->table = $container->get(Table::class);//從容器中取
//在think\Table的配置章節配置了兩個table,使用方式如下
//vendor\topthink\think-swoole\src\Table.php實現了__get魔術方法,因此直接table->表即可使用
//或者table->get(表名)取得該表
$this->u2fd = $this->table->u2fd;
$this->fd2u = $this->table->fd2u;
}
public function onConnect(Request $request)
{
//onOpen觸發的事件,傳入的是一個request對象
//模擬產生一個用戶編號
$uid = rand(100, 999);
//將uid與fd用table進行關聯,set方法的key必須為string類型
//通過websocket的上下文取得fd:$this->websocket->getSender()
$this->table->u2fd->set((string)$uid, ['fd' => $this->websocket->getSender()]);
//將fd與uid用table進行關聯
//注意傳入數組的key必須與tables中的columns的name值相同,否則無法寫入
$this->table->fd2u->set((string)$this->websocket->getSender(), ['uid' => $uid]);
//這里是對所有的連接發送一次廣播,消息為online,當前連接用戶的uid,在線數量,當前連接用戶的fd值
foreach ($this->table->u2fd as $row) {
$this->server->push($row['fd'], json_encode(['msg' => 'online', 'uid' => $uid, 'online_count' => $this->u2fd->count(), 'fd' => $this->websocket->getSender()], 320));
}
}
public function onClose()
{
//onClose觸發的事件
//將離線的用戶從關系表中移除
$uid = $this->table->fd2u->get((string)$this->websocket->getSender(), 'uid');
//注意傳入參數必須為string類型
$this->table->u2fd->del((string)$uid);
$this->table->fd2u->del((string)$this->websocket->getSender());
//廣播下線
foreach ($this->table->u2fd as $row) {
$this->server->push($row['fd'], json_encode(['msg' => 'offline', 'uid' => $uid, 'online_count' => $this->u2fd->count(), 'fd' => $this->websocket->getSender()], 320));
}
}
public function onPoint($event)
{
//點對點消息 即json中event值為point會觸發該方法
//json:{"event":"point","data":{"to":uid,"content":"wsmsg"}}
//用uid從列表中查找該用戶是否在線
$toFd = $this->table->u2fd->get((string)$event['to'], 'fd');
if ($toFd === false) {
if (isset($event['api'])) {
return 'offlone';
}
$this->server->push($this->websocket->getSender(), $event['to'] . ' is not online');
}
//判斷是否來自api消息
if (isset($event['api'])) {
//消息下放
$this->server->push($toFd, $this->assemblyData('point', ['sender' => $event['uid'] ?? 0, 'content' => $event['content']]));
} else {
//消息下放
$this->server->push($toFd, $this->assemblyData('point', ['sender' => $this->table->fd2u->get((string)$this->websocket->getSender(), 'uid'), 'content' => $event['content']]));
}
}
public function onPing()
{
//響應ping消息
//{"event":"ping","data":""}
$this->server->push($this->websocket->getSender(), $this->assemblyData('pong', []));
}
//more event function
//統一的消息格式
protected function assemblyData(string $event, array $data, int $code = 0): string
{
return json_encode(['event' => $event, 'data' => $data, 'code' => $code], 320);
}
}
~~~
</details>