1 開發需要環境 工欲善其事,必先利其器。在正式開發之前我們檢查好需要安裝的拓展,不要開發中發現這些問題,打斷思路影響我們的開發效率。 安裝 swoole 拓展包 安裝 redis 拓展包 安裝 laravel5.5 版本以上 如果你還不會用swoole就out了 2 Laravel 生成命令行 p ...
1 開發需要環境
工欲善其事,必先利其器。在正式開發之前我們檢查好需要安裝的拓展,不要開發中發現這些問題,打斷思路影響我們的開發效率。
- 安裝 swoole 拓展包
- 安裝 redis 拓展包
- 安裝 laravel5.5 版本以上
如果你還不會用swoole就out了
2 Laravel 生成命令行
- php artisan make:command SwooleDemo
class SwooleDemo extends Command { protected $signature = 'swoole:demo'; protected $description = '這是關於swoole的一個測試demo'; public function __construct() { parent::__construct(); } public function handle() { $this->line("hello world"); } }
我們分別運行 php artisan 指令和 php artisan swoole:demo 會看到關於這個命令的說明,和輸出 hello world。(laravel 命令行用法詳解)
3 命令行邏輯代碼
- 編寫一個最基礎的 swoole 命令行邏輯代碼
<?php namespace App\Console\Commands; use Illuminate\Console\Command; use Illuminate\Support\Facades\Redis; class SwooleDemo extends Command { // 命令名稱 protected $signature = 'swoole:demo'; // 命令說明 protected $description = '這是關於swoole websocket的一個測試demo'; // swoole websocket服務 private static $server = null; public function __construct() { parent::__construct(); } // 入口 public function handle() { $this->redis = Redis::connection('websocket'); $server = self::getWebSocketServer(); $server->on('open',[$this,'onOpen']); $server->on('message', [$this, 'onMessage']); $server->on('close', [$this, 'onClose']); $server->on('request', [$this, 'onRequest']); $this->line("swoole服務啟動成功 ..."); $server->start(); } // 獲取服務 public static function getWebSocketServer() { if (!(self::$server instanceof \swoole_websocket_server)) { self::setWebSocketServer(); } return self::$server; } // 服務處始設置 protected static function setWebSocketServer():void { self::$server = new \swoole_websocket_server("0.0.0.0", 9502); self::$server->set([ 'worker_num' => 1, 'heartbeat_check_interval' => 60, // 60秒檢測一次 'heartbeat_idle_time' => 121, // 121秒沒活動的 ]); } // 打開swoole websocket服務回調代碼 public function onOpen($server, $request) { if ($this->checkAccess($server, $request)) {\ self::$server->push($request->fd,"打開swoole服務成功!");\ } } // 給swoole websocket 發送消息回調代碼 public function onMessage($server, $frame) { } // http請求swoole websocket 回調代碼 public function onRequest($request,$response) { } // websocket 關閉回調代碼 public function onClose($serv,$fd) { $this->line("客戶端 {$fd} 關閉"); } // 校驗客戶端連接的合法性,無效的連接不允許連接 public function checkAccess($server, $request):bool { $bRes = true; if (!isset($request->get) || !isset($request->get['token'])) { self::$server->close($request->fd); $this->line("介面驗證欄位不全"); $bRes = false; } else if ($request->get['token'] !== "123456") { $this->line("介面驗證錯誤"); $bRes = false; } return $bRes; } // 啟動websocket服務 public function start() { self::$server->start(); } }
編寫 websoket js 代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>swoole測試</title> <meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"> </head> <body> <h1>這是一個測試</h1> </body> <script> var ws;//websocket實例 var lockReconnect = false;//避免重覆連接 var wsUrl = 'ws://{{$_SERVER["HTTP_HOST"]}}:9502?page=home&token=123456'; function initEventHandle() { ws.onclose = function () { reconnect(wsUrl); }; ws.onerror = function () { reconnect(wsUrl); }; ws.onopen = function () { //心跳檢測重置 heartCheck.reset().start(); }; ws.onmessage = function (event) { //如果獲取到消息,心跳檢測重置 //拿到任何消息都說明當前連接是正常的 var data = JSON.parse(event.data); heartCheck.reset().start(); } } createWebSocket(wsUrl); /** * 創建鏈接 * @param url */ function createWebSocket(url) { try { ws = new WebSocket(url); initEventHandle(); } catch (e) { reconnect(url); } } function reconnect(url) { if(lockReconnect) return; lockReconnect = true; //沒連接上會一直重連,設置延遲避免請求過多 setTimeout(function () { createWebSocket(url); lockReconnect = false; }, 2000); } //心跳檢測 var heartCheck = { timeout: 60000,//60秒 timeoutObj: null, serverTimeoutObj: null, reset: function(){ clearTimeout(this.timeoutObj); clearTimeout(this.serverTimeoutObj); return this; }, start: function(){ var self = this; this.timeoutObj = setTimeout(function(){ //這裡發送一個心跳,後端收到後,返回一個心跳消息, //onmessage拿到返回的心跳就說明連接正常 ws.send("heartbeat"); self.serverTimeoutObj = setTimeout(function(){//如果超過一定時間還沒重置,說明後端主動斷開了 ws.close();//如果onclose會執行reconnect,我們執行ws.close()就行了.如果直接執行reconnect 會觸發onclose導致重連兩次 }, self.timeout); }, this.timeout); }, header:function(url) { window.location.href=url } } </script> </html> 訪問前端頁面 (顯示如下說明前後端鏈接成功)