前言
????本次僅記錄 websocket 與 swoole 配合打造一個小型的聊天室,功能簡陋,但是可以作為一個很好的入門案例。
項目簡介
????本來就是作為一個很小的案例來寫的,包含的功能點也不是那多,索性就按照最最最低配置來搞。
-
能夠顯示聊天消息的聊天區(qū),同時兼顧顯示鏈接狀態(tài),當(dāng)前是否連接成功,或者服務(wù)端是否斷開連接,而前端不知道的狀況。
-
一個輸入框,純粹的輸入框 ?
-
點擊按鈕發(fā)送不刷新頁面,同時清空當(dāng)前輸入框內(nèi)容,就簡單的一個 button 而已,點擊執(zhí)行,不支持回車發(fā)送。
-
收到消息,滾動條自動觸底,這個功能在某些使用場景是方便的,但又會造成某些場景使用不方便,方便在于有新消息不需要人工滾動,不方便在于,可能你在看歷史消息,它自動觸底了…還需要根據(jù)自己實際需求優(yōu)化一下下。
-
隨機昵稱,當(dāng)然不需要保存,刷新即丟,在收到消息如果是自己發(fā)送的,則顯示 [ 我 ] 在某某時候發(fā)送了某某消息,而不是顯示昵稱字符串。
項目環(huán)境
直接粘貼復(fù)制的
composer?create-project?topthink/think?tpcd?tpcomposer?require?topthink/think-swoole
????因為是測試項目,所有的都是默認安裝,在安裝完之后,訪問前端頁面,使用 view 方法會報錯,百度一下就有解決方案了。
webSocket 的使用
參考文檔:https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket
- onopen() 發(fā)起連接,連接成功后執(zhí)行。
- onclose() 連接斷開后執(zhí)行。
- onmessage() 收到服務(wù)端消息后執(zhí)行。
- onerror() 服務(wù)器異常執(zhí)行。
其實,webSocket 就這些常用方法,也沒啥特殊要求的,他就是作為一個保持連接,接收服務(wù)器狀態(tài)的一個瀏覽器的 API 存在,非常簡潔方便。
前端頁面代碼:
nbsp;html>????<meta>????<title>打工人聊天室</title>???<!--需要引入jq 文件--><style> .content { height: 400px; max-width: 400px; overflow: auto; border-radius: 5px; border: 1px solid #f0f0f0; }</style>????????????<div>????????????????<p>聊天區(qū)域</p>????????????</div>????????????你好打工人:<samp>昵稱</samp>?<br>????????????本次連接FD:?<samp></samp>?<br>????????????<input>????????????<input>????????????<button>發(fā)送</button>
JS 代碼:
????在服務(wù)器信息回執(zhí)時,會有第一次連接回執(zhí),還是服務(wù)端發(fā)送消息回執(zhí)的狀態(tài)區(qū)別,通過 msgType 來分辨,如果是第一次連接的回執(zhí)消息,則把 FD 做一個頁面留存,并不顯示在聊天消息區(qū),如果收到的是消息回執(zhí),就直接顯示到聊天消息區(qū)。
????還有就是,前后端相互通信發(fā)送的東西,都是字符串性質(zhì)最優(yōu),我前端處理的方法是先組合成一個對象,然后轉(zhuǎn) 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); //點擊發(fā)送 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> 服務(wù)器已連接,開始聊天吧 '); }; //鏈接斷開 websocket.onclose = function (evt) { $("#content >p:last-child").after('<p> 服務(wù)器已斷開,請重新連接 '); }; //收到服務(wù)器消息 websocket.onmessage = function (evt) { //握手成功后,會接受到服務(wù)端返回的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 + ''); //接收到消息自動觸底 scrolltest(); break; } }; //服務(wù)器異常 websocket.onerror = function (evt, e) { $("#content >p:last-child").after('<p> 服務(wù)器異常 '); };</script>
服務(wù)端代碼
????服務(wù)端需要 callback 前端過來的消息,轉(zhuǎn)成對象數(shù)據(jù),然后增加點自定義數(shù)據(jù)直接原樣返回,并且群發(fā)到前端。
<?php //創(chuàng)建WebSocket Server對象,監(jiān)聽0.0.0.0:9502端口 $ws = new SwooleWebSocketServer('0.0.0.0', 9502); //監(jiān)聽WebSocket連接打開事件 $ws->on('open',?function?($ws,?$request){????????$fd?=?$request->fd;????????$data?=?json_encode([????????????'fd'?=>?$request->fd,????????????'msgType'?=>?1??//代表第一次連接,前端處理fd????????]);????????$ws->push($request->fd,?$data);????});????//監(jiān)聽WebSocket消息事件????$ws->on('message',?function?($ws,?$frame)?{????????$stats?=?$ws->stats();????????//格式化接收到j(luò)son????????$data?=?json_decode($frame->data);????????//?原基礎(chǔ)上不動,增加一些自定義????????$data->msgType?=?2;?//代表服務(wù)器端回復(fù)????????$data->time?=?date('Y-m-d?H-i-s');????????$data?=?json_encode($data);????????//因為是聊天室,所以包括自己都需要收到回執(zhí),就直接群發(fā)?swoole?提供?connections?方法?包含了所有在線的?fd????????foreach?($ws->connections?as?$conn_fd){????????????$ws->push($conn_fd,$data);????????}????});????//監(jiān)聽WebSocket連接關(guān)閉事件????$ws->on('close',?function?($ws,?$fd)?{//????????echo?"client-{$fd}?is?closedn";????});????$ws->start();
代碼齊全之后,接下來就只需要在控制臺執(zhí)行以下 PHP 文件就行。
然后前臺直接訪問你的網(wǎng)站地址,我的是本地 127.0.0.1
多開幾個窗口模擬多個用戶,然后發(fā)送消息測試即可:
你好,打工人。
代碼很簡單,難度不大,但是可以很簡潔的反應(yīng)出 webScoket 和 Swoole 的一種強大。