1. 程式人生 > >WorkerMan學習篇:websocket+workerman聊天功能設計(一):簡單認證

WorkerMan學習篇:websocket+workerman聊天功能設計(一):簡單認證

初步設計如下

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();

這裡寫圖片描述

伺服器控制檯列印:
這裡寫圖片描述