WebSocket 協議在2008年誕生,2011年成爲國際標準。全部瀏覽器都已經支持了。javascript
它的最大特色就是,服務器能夠主動向客戶端推送信息,客戶端也能夠主動向服務器發送信息,是真正的雙向平等對話,屬於服務器推送技術的一種。php
需求是:用戶停留頁面 15 分鐘,且沒有任何操做,則彈出登錄窗口,讓用戶從新登錄。html
通常這樣的需求實現多爲長鏈接輪詢,會有瀏覽器的卡頓、服務端消耗及不容易維護等問題。前端
後來發現 websocket 這樣的通信方式,主要有如下優勢:java
目標已經選定,那麼如何實現呢?laravel
PHP 已經有很是好用的異步網絡通訊框架 swoole,節省了本身實現 websocket 服務的時間。個人使用的是 laravel 框架,最終選擇了 laravel-swoole 擴展。c++
引入 laravel-swoole 擴展包 wiki 。啓用 websocket.enabled
及其餘相應的配置,經過下面的命名能夠很是方便的管理服務:git
php artisan swoole:http {start|stop|restart|reload|infos}
修改配置文件中的默認 handler
配置爲自定義的類:主要是爲了自定義 websocket
的生命週期中的一些回調。github
/* |-------------------------------------------------------------------------- | Websocket handler for onOpen and onClose callback | Replace this handler if you want to customize your websocket handler |-------------------------------------------------------------------------- */ 'handler' => \App\Listeners\Swoole\WebsocketHandler::class, /* |-------------------------------------------------------------------------- | Default frame parser | Replace it if you want to customize your websocket payload |-------------------------------------------------------------------------- */ 'parser' => SwooleTW\Http\Websocket\SocketIO\SocketIOParser::class,
當配置完成後,會在 routes
目錄中添加一個名爲 websocket.php
的文件。能夠很是方便像定義 laravel 路由同樣,定義各類事件。例如:web
//Websocket::on('open', function ($websocket, Request $request) { // Log::info('websocket','open 111 +' . $websocket->getSender()); //}); // //Websocket::on('connect', function ($websocket, Request $request) { // Log::info('websocket','Connected ++ 222' . $websocket->getSender()); // // called while socket on connect. //}); // //Websocket::on('disconnect', function ($websocket) { // Log::info('websocket','Disconnected ++ 333' . $websocket->getSender()); // // called while socket on disconnect //}); // 在 UserController 中的 checkLogin 方法上會帶有$websocket, $data這兩個參數。 Websocket::on('loginCheck', "App\Http\Controllers\Api\UserController@checkLogin"); Websocket::on('logout', "App\Http\Controllers\Api\UserController@sendLogout");
public function checkLogin($websocket, $data) { if (empty($data['holding'])) { $websocket->emit('message', ['code' => self::HTTP_UNPROCESSABLE_ENTITY, 'message' => "參數錯誤"]); return false; } $flag = true; $step = 1; while ($flag) { $step++; if ( ! $this->validateLoginStatus($data['holding'])) { $websocket->emit('message', ['code' => self::HTTP_UNAUTHORIZED, 'message' => "登錄超時"]); unset($step); $flag = false; }else { if($step === 1) { $websocket->emit('message', ['code' => self::HTTP_OK, 'message' => "success"]); } } sleep(1); } }
這裏必定要注意數據包的結構,以前就踩了比較多的坑,API docs 才找到正確的結構:
var websocket = new WebSocket("ws://127.0.0.1:1215"); websocket.onopen = function (evt) { console.log("已鏈接websocket服務器"); // 這裏比較關鍵,通道創建後,能夠進很是方便的進行輪詢 setInterval(function() { if (websocket.bufferedAmount == 0) var data = {"holding": "eyJLQNDqj0y473pCJ6zjMTUyOTk5NzU1MgnVMQ==$d84XkeMCv7umajhMRiU"}; websocket.send(encodeMessage('loginCheck', data)); }, 50); }; // 監聽消息體 websocket.onmessage = function (evt) { console.log(decodeMessage(evt.data)) }; // 監聽關閉消息 websocket.onclose = function (evt) { console.log("websocket close"); }; //監聽鏈接錯誤信息 websocket.onerror = function (evt) { console.log(evt); }; function decodeMessage(str) { return JSON.parse(str.substring(2))[1] || []; } function encodeMessage(event, data) { return JSON.stringify([ event, data ]) }
由於 swoole 的安裝依賴 php
的 sockets
模塊的開啓。
中間報錯,須要安裝如下依賴:
yum -y install gcc postgresql-devel gcc-c++
下載 swoole 擴展源碼,安裝 安裝步驟 進行安裝便可。
查看當前
$ netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' TIME_WAIT 814 CLOSE_WAIT 1 FIN_WAIT1 1 ESTABLISHED 634 SYN_RECV 2 LAST_ACK 1
經常使用的三個狀態是:ESTABLISHED
表示正在通訊,TIME_WAIT
表示主動關閉,CLOSE_WAIT
表示被動關閉。
查看進程數
$ ps -eaf |grep "swoole" | grep -v "grep"| awk '{print $2}'|wc -l
批量刪除進程:
$ ps -eaf |grep "swoole" | grep -v "grep"| awk '{print $2}'|xargs kill -9
重啓服務。