「swoole實驗版聊天室」是依據一堂swoole培訓課內容改編的,結合了bootstrap前端框架、redis數據庫、jquery框架等實現基本功能,只是體現了swoole的應用,並非爲了專門寫個聊天室。php
本程序開發環境:
1.ubuntu16.04.1
2.PHP Version 7.2.21
3.swoole Version 4.4.3
4.Redis Version 4.0.2
安裝步驟(略)。
效果以下:css
代碼以下:html
index.php文件前端
<!DOCTYPE html> <html> <head> <title>swoole實驗版聊天室</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="cache-control" content="no-cache"> <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css"> <script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script> <script src="https://cdn.staticfile.org/popper.js/1.12.5/umd/popper.min.js"></script> <script src="https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"></script> <link rel="stylesheet" href="https://cdn.staticfile.org/font-awesome/4.7.0/css/font-awesome.css"> <style> body{background-color: #191970;} @media screen and (min-width: 992px) { div{display: none} body {background: #fff} body:before { content: "不支持此設備瀏覽!"; position: absolute; left: 10px; top: 30px } } @media screen and (min-width: 768px) and (max-width: 991px) { div{display: none} body {background: #fff} body:before { content: "不支持此設備瀏覽!"; position: absolute; left: 10px; top: 30px } } @media screen and (min-width: 576px) and (max-width: 767px) { #result{ width: 100%; height: 300px; overflow-y: scroll; margin: 0px; padding: 5px; } #is-open{ display: inline-block; border: solid 1px #dcdcdc; padding: 6px; margin: 6px 0px; border-radius: 8px; color: #fff; } .ms { display:inline-block; width:80% } .msl { display:inline-block; width:40%; background-color: #66cdaa } .btn { display:inline-block; float: right; width:20% } .mess{ color: #00fa9a; text-align: right } .mess-1{ display: inline-block; word-wrap:break-word; word-break:break-all; color: #2f4f4f; background-color:#f4a460; padding: 8px; border-radius: 8px; text-align: left } .mess-1-1, .mess-2-2 { display: inline-block; color: #000; font-weight: bold; background-color: #add8e6; } .mess-1-1{ padding: 8px; border-radius: 8px } .mess-2-2{ padding: 8px; border-radius: 8px } .mess-2{ display: inline-block; word-wrap:break-word; word-break:break-all; color: #696969; background-color:#fff; padding: 8px; border-radius: 8px } .tot{ display: inline-block; width: 40%; word-wrap:break-word; word-break:break-all; color: #fff; float: right; margin-top: 3px; } .bottom-box{ position: fixed; left: 0px; bottom: 0px; width:100%; background-color: #191970; padding-top: 10px } } @media screen and (min-width: 300px) and (max-width: 575px) { #result{ width: 100%; height: 300px; overflow-y: scroll; margin: 0px; padding: 5px; } #is-open{ display: inline-block; border: solid 1px #dcdcdc; padding: 6px; margin: 6px 0px; border-radius: 8px; color: #fff; } .ms { display:inline-block; width:80% } .msl { display:inline-block; width:40%; background-color: #66cdaa } .btn { display:inline-block; float: right; width:20% } .mess{ color: #00fa9a; text-align: right } .mess-1{ display: inline-block; word-wrap:break-word; word-break:break-all; color: #2f4f4f; background-color:#f4a460; padding: 8px; border-radius: 8px; text-align: left } .mess-1-1, .mess-2-2 { display: inline-block; color: #000; font-weight: bold; background-color: #add8e6; } .mess-1-1{ padding: 8px; border-radius: 8px } .mess-2-2{ padding: 8px; border-radius: 8px } .mess-2{ display: inline-block; word-wrap:break-word; word-break:break-all; color: #696969; background-color:#fff; padding: 8px; border-radius: 8px } .tot{ display: inline-block; width: 40%; word-wrap:break-word; word-break:break-all; color: #fff; float: right; margin-top: 3px; } .bottom-box{ position: fixed; left: 0px; bottom: 0px; width:100%; background-color: #191970; padding-top: 10px } } </style> </head> <body> <div class="container"> <span id="is-open">服務器正在鏈接......</span> <span class="tot">當前共有<span id="total">0</span>個窗口在鏈接!</span> <p id="result"></p> <form> <div class = "bottom-box"> <p><span style="color: #fff">暱稱:</span><input type="text" id="username" class="form-control msl"/></p> <p> <input id="message" class="form-control ms"> <button id="btn" type="button" class="btn btn-primary">發送</button> </p> </div> </form> </div> <script> $(document).ready(function(){ var timeOut; var arrResult = []; // 設置cookie函數 function setCookie(cname,cvalue,exdays){ var d = new Date(); d.setTime(d.getTime()+(exdays*24*60*60*1000)); var expires = "expires="+d.toGMTString(); document.cookie = cname + "=" + cvalue + "; " + expires; } // 取出cookie數據函數 function getCookie(cname){ var name = cname + "="; var ca = document.cookie.split(';'); for(var i=0; i<ca.length; i++) { var c = ca[i].trim(); if (c.indexOf(name)==0) return c.substring(name.length,c.length); } return ""; } /* 刷新頁面時從取出數據初始化頁面 */ // 初始化插入暱稱 var usrName = (getCookie("userName") != null)?getCookie("userName"):""; $("#username").val(usrName); setTimeout(function(){ // 初始化滾動消息到最新 $("#result").scrollTop(result.scrollHeight); },500); // 刷新頁面時從redis數據庫取出數據初始化頁面 $.post("./ajax.php",{},function(ajaxData){ var m = JSON.parse(ajaxData); for(key in m){ arrResult.push(m[key]); } $("#result").html(arrResult.join('')); }); /* 初始化數據結束 */ /* websocket通訊 */ var ws = new WebSocket("ws://47.94.224.241:9503"); ws.onopen=function(){ $("#is-open").html('服務器鏈接成功......'); clearInterval(timeOut); } ws.onmessage=function(res){ var data=JSON.parse(res.data); if(data.userId){ window.userId = data.userId; } if(data.total){ $("#total").html(data.total); return false; } if( data.cookieUsrName == getCookie("userName") ) { arrResult.push('<p class="mess"><span class="mess-1">' + data.message + '</span><span class="mess-1-1">我</span></p>'); }else{ arrResult.push('<p><span class="mess-2-2">' + data.username + '</span><span class="mess-2">' + data.message + '</span></p>'); } // while()裏的49要與ajax.php裏的lpush方法裏的參數49要統一 while ( arrResult.length > 49) { arrResult.shift(); } // 顯示在界面上 var val = arrResult.join(""); $("#result").html(val); $("#result").scrollTop(result.scrollHeight); } ws.onclose=function(){ $("#is-open").html(''); $("#is-open").html('服務器已斷開......'); // 斷開鏈接刷新頁面 timeOut = setInterval(function(){ location.replace(location.href); }, 1000); } btn.onclick=function(){ if($("#username").val() == '' || $("#message").val() == '') { // 打開模態框 $('#myModal').modal('show'); }else{ /* * 組裝數據,數據是在js裏組裝的,swoole服務器只是轉發了一下數據並存入redis數據庫 * * 判斷「我」的方法: * 1. 一種是判斷fromuserid,能夠判斷到具體窗口,一個瀏覽器打開3個窗口就是3個不一樣的「我」; * 2. 另外一種是判斷cookie或session裏存儲的用戶名,即cookieUsrName鍵,一個瀏覽器認 * 爲是一個用戶,這裏是用暱稱代替了。 */ var data={ fromuserid: window.userId, username: username.value, message: message.value, cookieUsrName: getCookie("userName") } ; //對象轉JSON字符串發送到服務器 ws.send( JSON.stringify(data) ); message.value=''; } } username.onkeyup = function() { //暱稱存入cookie setCookie("userName", $("#username").val(), 7); } }); </script> <!-- 模態框 --> <div class="modal fade" id="myModal"> <div class="modal-dialog"> <div class="modal-content"> <!-- 模態框頭部 --> <div class="modal-header"> <h4 class="modal-title"> <i class="fa fa-exclamation-circle fa-lg" style="color: red"></i> 錯誤提示: </h4> <button type="button" class="close" data-dismiss="modal">×</button> </div> <!-- 模態框主體 --> <div class="modal-body"> 「暱稱」和「消息」內容都不容許爲空,請從新輸入! </div> <!-- 模態框底部 --> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">關閉</button> </div> </div> </div> </div> </body> </html>
swooleServer.phpreact
<?php $ws = new swoole_websocket_server("0.0.0.0", 9503, SWOOLE_PROCESS); $ws->set(array( 'reactor_num' => 2, //reactor thread num 'worker_num' => 4, //worker process num 'backlog' => 128, //listen backlog 'max_conn' => 10000, 'max_request' => 50, 'dispatch_mode' => 1, 'daemonize' => 1 )); $ws->on('open', function ($ws, $request) { $tot = count($ws->connections); $ws->push($request->fd, json_encode(['userId' => $request->fd, 'total' => $tot])); }); $ws->on('message', function ($ws, $frame) { // 每一個用戶發送的消息向全部用戶轉發 foreach($ws->connections as $value) { $ws->push($value, $frame->data); }
// 向redis數據庫寫入數據,數據類型「列表」 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->lpush("messages", $frame->data); // 超過100條數據開始刪除舊數據 while($redis->llen("messages") > 100) { $redis->rpop("messages"); } }); $ws->on('close', function ($ws, $fd) { $i = 0; foreach($ws->connections as $v) { if($v == $fd) { unset($ws->connections[$i]); } $i++; } }); $ws->start(); ?>
ajax.phpjquery
<?php $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $arrList = $redis->lrange("messages", 0, 20); $arrValue = []; while(count($arrList) > 0){ $v = array_pop($arrList); $arrVal = json_decode($v, true); if($arrVal['cookieUsrName'] == $_COOKIE['userName']) { array_push($arrValue, '<p class="mess"><span class="mess-1">' . $arrVal['message'] . '</span><span class="mess-1-1">我</span></p>'); }else{ array_push($arrValue, '<p><span class="mess-2-2">' . $arrVal['username'] . '</span><span class="mess-2">' . $arrVal['message'] . '</span></p>'); } } echo json_encode($arrValue); ?>
能夠經過下面的頁面查看redis數據庫裏的內容,不用去服務器端查看效果。web
showRedis.phpajax
<?php //鏈接本地的 Redis 服務 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); // 獲取存儲的數據並輸出 $arList = $redis->lrange("messages", 0 ,100);
echo '當前顯示內容->' . json_decode($arList[0], true)["username"] . ":「" . json_decode($arList[0], true)["message"] . "」"; echo "<pre>"; print_r($arList); echo "</pre>";?>