WorkerMan學習篇:websocket+workerman聊天功能設計(一):簡單認證
阿新 • • 發佈:2019-01-08
初步設計如下
1、websocket客戶端連線服務端 是無腦的(這很重要)
2、服務端需要記錄連線進來的所有客戶端(方便日後統一廣播訊息)
3、服務端肯定能獲得客戶端ip。為此我們還需要客戶端加入使用者暱稱來區分
4、服務端可以無腦的向客戶端傳送訊息。但是客戶端怎麼區分?
於是我們自己定義一個格式來完成。
4.1、普通訊息我們用msg:xxx
。如果開頭是msg:
那麼後面的內容統統認定為普通訊息
4.2、認證訊息notice:xxx
,比如服務端認可客戶端連線上了,則傳送notice:
開頭的訊息
4.3、客戶端傳送login:暱稱
,代表連線服務端,並進行認證。
構建websocket客戶端介面
<div id="txtcontent" style="width: 500px;height: 250px;border: 1px solid gray"></div>
<div>所有使用者:<select id="listuers"></select></div>
<div>你的暱稱:<input type="text" id="username" /></div>
<div>
回覆內容:
<textarea style ="width: 500px;height: 100px" id="txtmsg"></textarea>
</div>
<div>
<button onclick="connectServer()">連線伺服器</button>
<button onclick="send()">傳送訊息</button>
</div>
websocket客戶端建立websocket連線,傳送訊息到服務端的程式碼:
<script>
//建立一個socket例項
var socket = null; //初始為null
var isLogin = false; //是否登入到伺服器上
//定義一個連服務的函式
function connectServer(){
var username = document.getElementById('username').value;
if (username == ''){
alert('使用者暱稱必填');
}
socket = new WebSocket("ws://10.211.55.13:9090");
socket.onopen = function() {
socket.send('login:' + username);
};
socket.onmessage = function(e) {
var getMsg = e.data;
if(/^notice:success$/.test(getMsg)){ //伺服器驗證通過
isLogin = true;
}else if(/^msg:/.test(getMsg)){ //代表是普通訊息
var p = document.createElement('p');
p.innerHTML = '<span>收到訊息:</span>' + getMsg.replace('msg:','');
document.getElementById('txtcontent').appendChild(p);
}
};
socket.onclose = function(){
isLogin = false;
}
}
//傳送訊息
function send(){
if (!isLogin){
alert('請先通過伺服器驗證');
}
var msg = document.getElementById('txtmsg').value;
//console.log(msg);
socket.send('msg:' + msg); //傳送訊息到服務端
//顯示我們的訊息到div中
var p = document.createElement('p');
p.innerHTML = '<span>回覆訊息:</span>' + msg;
document.getElementById('txtcontent').appendChild(p);
}
</script>
服務端程式碼:
<?php
//本機IP是10.211.55.13
//需要監聽的埠是 9090
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Worker;
require 'workerman/Autoloader.php';
$clients = []; //儲存客戶端資訊
// 建立一個Worker監聽9090埠,使用websocket協議通訊
$ws_worker = new Worker("websocket://10.211.55.13:9090");
// 啟動4個程序對外提供服務
$ws_worker->count = 4;
// 當收到客戶端發來的資料後
$ws_worker->onMessage = function($connection, $data)
{
//這裡用global的原因是:php是有作用域的,我們是在onMessage這個回撥還是裡操作外面的陣列
//想要改變作用域外面的陣列,就global一下
global $clients;
//驗證客戶端使用者名稱在3-20個字元
if(preg_match('/^login:(\w{3,20})/i',$data,$result)){ //代表是客戶端認證
$ip = $connection->getRemoteIp();
if(!array_key_exists($ip,$clients)){ //必須是之前沒有註冊過
$clients[$ip] = $result[1]; //把新使用者儲存起來
// 向客戶端傳送資料
$connection->send('notice:success'); //驗證成功訊息
$connection->send('msg:welcome '.$result[1]); //普通訊息
echo $ip .':' .$result[1] .'login' . PHP_EOL; //這是為了演示,控制檯列印資訊
}
}elseif(preg_match('/^msg:(.*?)/isU',$data,$msgset)){ //代表是客戶端傳送的普通訊息
if(array_key_exists($connection->getRemoteIp(),$clients)){ //必須是之前驗證通過的客戶端
echo 'get msg:' . $msgset[1] .PHP_EOL; //這是為了演示,控制檯列印資訊
if($msgset[1] == 'nihao'){
//如果收到'nihao',就給客戶端傳送'nihao 使用者名稱'
//給客戶端傳送普通訊息
$connection->send('msg:nihao '.$clients[$connection->getRemoteIp()]);
}
}
}
// 設定連線的onClose回撥
$connection->onClose = function($connection) //客戶端主動關閉
{
global $clients;
unset($clients[$connection->getRemoteIp()]);
echo "connection closed\n";
};
};
// 執行worker
Worker::runAll();
伺服器控制檯列印: