前言
這次只記錄 webSocket 與 Swoole 配合打造一個小型的聊天室,功能簡陋,但是可以作為一個很好的入門案例。
專案簡介
本來就是作為一個很小的案例來寫的,包含的功能點也不是那多,索性就按照最最低配置來搞。
能夠顯示聊天訊息的聊天區,同時兼顧顯示連結狀態,目前是否連接成功,或服務端是否斷開連接,而前端不知道的狀況。
一個輸入框,純粹的輸入框?
#點擊按鈕發送不刷新頁面,同時清空目前輸入框內容,就簡單的一個button 而已,點擊執行,不支援回車發送。
收到訊息,滾動條自動觸底,這個功能在某些使用場景是方便的,但又會造成某些場景使用不方便,方便在於有新訊息不需要人工滾動,不方便在於,可能你在看歷史消息,它自動觸底了…還需要根據自己實際需求優化一下下。
隨機暱稱,當然不需要保存,刷新即丟,在收到訊息如果是自己發送的,則顯示[ 我] 在某某時候發送了某某訊息,而不是顯示暱稱字串。
專案環境
直接貼上複製的
composer create-project topthink/think tpcd tpcomposer require topthink/think-swoole
因為是測試項目,所有的都是預設安裝,在安裝完之後,造訪前端頁面,使用view 方法會報錯,百度一下就有解決方案了。
webSocket 的使用
參考文件:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
- onopen() 發起連接,連線成功後執行。
- onclose() 連線中斷後執行。
- onmessage() 收到服務端訊息後執行。
- onerror() 伺服器異常執行。
其實,webSocket 就這些常用方法,也沒啥特殊要求的,他就是作為一個保持連接,接收伺服器狀態的一個瀏覽器的 API 存在,非常簡潔方便。
前端頁碼:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>打工人聊天室</title> <!--需要引入jq 文件--></head><style> .content { height: 400px; max-width: 400px; overflow: auto; border-radius: 5px; border: 1px solid #f0f0f0; }</style><body> <div id="content" class="content"> <p>聊天区域</p> </div> 你好打工人:<samp id="nickname">昵称</samp> <br> 本次连接FD: <samp id="fd-samp"></samp> <br> <input type="text" id="msg"> <input type="hidden" id="fd" value=""> <button id="send" onclick="send()">发送</button></body></html>
JS 代碼:
在伺服器資訊回執時,會有第一次連接回執,還是服務端發送訊息回執的狀態區別,透過msgType 分辨,如果是第一次連線的回執訊息,則把FD 做一個頁面留存,並不顯示在聊天訊息區,如果收到的是訊息回執,就直接顯示到聊天訊息區。
還有就是,前後端互相通訊發送的東西,都是字串性質最優,我前端處理的方法是先組合成一個對象,然後轉 JSON 串。
<script> //滚动条最底部 function scrolltest() { var div = document.getElementById("content"); div.scrollTop = div.scrollHeight; } var wsServer = 'ws://127.0.0.1:9502'; var websocket = new WebSocket(wsServer); var nickname = Math.random().toString(36).substr(2); thisFd = ''; $('#nickname').html(nickname); //点击发送 function send() { var msg = $('#msg').val(); var data = { 'nickname': nickname, 'fd': thisFd, 'data': msg } //生成json 方便后台接收以及使用 var data = JSON.stringify(data); websocket.send(data); //然后清空 $('#msg').val(''); } //链接成功 websocket.onopen = function (evt) { $("#content >p:last-child").after('<p> 服务器已连接,开始聊天吧 </p>'); }; //链接断开 websocket.onclose = function (evt) { $("#content >p:last-child").after('<p> 服务器已断开,请重新连接 </p>'); }; //收到服务器消息 websocket.onmessage = function (evt) { //握手成功后,会接受到服务端返回的fd ,msgType = 1 //字符串格式化成json var data = eval('(' + evt.data + ')'); // console.log(evt.data); switch (data.msgType) { case 1: thisFd = data.fd; $('#fd-samp').html(thisFd); $('#fd').val(thisFd); break; case 2: if (data.nickname == nickname) { data.nickname = '我'; } $("#content >p:last-child").after('<p>' + data.nickname + ' 在 ' + data.time + ' 说:<br>' + data.data + '</p>'); //接收到消息自动触底 scrolltest(); break; } }; //服务器异常 websocket.onerror = function (evt, e) { $("#content >p:last-child").after('<p> 服务器异常 </p>'); };</script>
服務端程式碼
服務端需要 callback 前端過來的訊息,轉換為物件數據,然後增加點自訂資料直接原樣返回,並且群發到前端。
<?php //创建WebSocket Server对象,监听0.0.0.0:9502端口 $ws = new Swoole\WebSocket\Server('0.0.0.0', 9502); //监听WebSocket连接打开事件 $ws->on('open', function ($ws, $request){ $fd = $request->fd; $data = json_encode([ 'fd' => $request->fd, 'msgType' => 1 //代表第一次连接,前端处理fd ]); $ws->push($request->fd, $data); }); //监听WebSocket消息事件 $ws->on('message', function ($ws, $frame) { $stats = $ws->stats(); //格式化接收到json $data = json_decode($frame->data); // 原基础上不动,增加一些自定义 $data->msgType = 2; //代表服务器端回复 $data->time = date('Y-m-d H-i-s'); $data = json_encode($data); //因为是聊天室,所以包括自己都需要收到回执,就直接群发 swoole 提供 connections 方法 包含了所有在线的 fd foreach ($ws->connections as $conn_fd){ $ws->push($conn_fd,$data); } }); //监听WebSocket连接关闭事件 $ws->on('close', function ($ws, $fd) {// echo "client-{$fd} is closed\n"; }); $ws->start();
程式碼齊全之後,接下來就只需要在控制台執行以下 PHP 檔案就行。
然後前台直接訪問你的網站地址,我的是本地127.0.0.1
多開幾個視窗模擬多個用戶,然後發送訊息測試即可:
你好,打工人。
程式碼很簡單,難度不大,但可以很簡潔的反應出 webScoket 和 Swoole 的一種強大。