swoole 的練習 demo(3)- 簡陋的 websocket 專案

yyy123456發表於2022-08-31

一直不能下決心好好學習,仔細研究一下,決定用盡量降低難度曲線的方法,從易到難,一步一步的學習,所以整了個demo專案。

git倉庫和使用步驟

確保能看到swoole,在 php -m 命令中
php -m 
git clone https://github.com/lang123789/swoole_demo.git
然後設定了標籤,本文對應 v3.0
cd swoole_demo
git checkout v3.0

有提示錯誤之類,但程式碼已經切換到 v3.0 了。

## 安裝類庫,生成 autoload.php 檔案
composer install
## 啟動 websocket 服務
php src/WebSocket/run.php

需求

4、任意一個前端頁面上線,則伺服器後臺列印“有人上線了,使用者id,使用者名稱稱”。
5、如果前端頁面關閉,就是離線了,則伺服器後臺列印有人離線。

測試網址

127.0.0.1/index.php?id=1
127.0.0.1/index.php?id=2

主要程式碼

客戶端主要程式碼

客戶端程式碼主要在 index.php 中

window.onload = function(){
     activate_chat_js();
     var wsServer = 'ws://{$JS_IP}:9501';
     var websocket = new WebSocket(wsServer); 
     websocket.onopen = function (evt) { 
         console.log("Connected to WebSocket server.");
         websocket.send("my_id|{$user_id}");
     };

    websocket.onclose = function (evt) { 
        console.log("Disconnected");
    };
    websocket.onmessage = function (evt) { 
       console.log('Retrieved data from server: ' + evt.data);
       var user = JSON.parse(evt.data);
       if (user.type=='my_id'){
            system_chatWindow(user.message);
       }
    };

    websocket.onerror = function (evt, e) { 
       console.log('Error occured: ' + evt.data);
    };    
}

服務端主要程式碼

class WebSocketServer
{
  private $config;
  private $table;
  private $server;

  private $user_all;

  public function __construct() {
    // 例項化配置
    // 記憶體表 實現程式間共享資料,也可以使用redis替代
    $this->createTable();
    $this->config = Config::getInstance();
    $this->user_all = $this->config['socket']['user_all'];
  }

  public function run() {  
     $this->server = new \swoole_websocket_server(
       $this->config['socket']['host'],
       $this->config['socket']['port']
     );

     $this->server->on('open', [$this, 'open']);
     $this->server->on('message', [$this, 'message']);
     $this->server->on('close', [$this, 'close']);
     $this->server->start();
  }

  public function open(\swoole_websocket_server $server, \swoole_http_request $request) {  
     echo "有人上線\n";
  }

  public function message(\swoole_websocket_server $server, \swoole_websocket_frame $frame) {
     $data = $frame->data;
     //這裡使用者發來的資訊已經分型別了。my_id開頭,說明是系統自動從客戶端傳送的資訊,用於識別身份。

     if (preg_match( '#my_id|#', $data )) {
       $user_id= explode("|",$data)[1] ;
       $arr=$this->user_all;
       $user_name = isset( $arr[$user_id] )? $arr[$user_id] :'';
       if (!$user_name){
          //如果使用者不存在,則w我直接關閉連線。
          echo "非法連線。使用者id:".$user_id;
          $server->close($frame->fd);
          return;
       }

       $user = [
          'fd' => $frame->fd,
          'user_name' => $user_name,
          'user_id' => strval($user_id),
       ];
       echo "有個人剛上線,資料:".json_encode( $user, JSON_UNESCAPED_UNICODE );
       // 放入記憶體表
       $this->table->set($frame->fd, $user);

       $server->push($frame->fd, json_encode([
          'type' => 'my_id',
          'message' => '歡迎您,'.$user_name,
       ]));
    }

 }

  /**
    * 客戶端關閉的時候
   */  
   public function close(\swoole_websocket_server $server, int $fd) {  
      $user = $this->table->get($fd);
      echo "有人下線,資料:".json_encode( $user, JSON_UNESCAPED_UNICODE );
      $this->table->del($fd);
  }

  /**
    * 建立記憶體表
   */
  private function createTable() {  
    $this->table = new \swoole_table(1024);
    $this->table->column('fd', \swoole_table::TYPE_INT);
    $this->table->column('user_name', \swoole_table::TYPE_STRING, 255);
    $this->table->column('user_id', \swoole_table::TYPE_STRING, 255);
    $this->table->create();
  }

目錄 config 下的 socket.php 是配置檔案,決定了伺服器在哪個埠啟動 websocket 服務。
以及面向哪些ip開放。
WebSocket 目錄下的 Config.php 是對 config檔案做的封裝,用處不大。
WebSocketServer.php 是核心檔案。

程式碼說明

客戶端首頁用php取代html,這樣可以填寫使用者id。
php程式碼接受請求引數id,代表了使用者id。
用陣列代替查資料庫,實現功能。
實現了簡陋的 websocket 連線功能。

流程和原理說明

(1)我們在伺服器的 shell 執行 php src/WebSocket/run.php,shell被同步阻塞。
(2)瀏覽器用 http 協議請求首頁 http://127.0.0.1/index.php?id=1,引數帶了使用者id
(3)伺服器先確認使用者是否存在,然後返回首頁。
(4)在瀏覽器這裡,var websocket = new WebSocket(wsServer); 這句話被執行。
(5)瀏覽器用 ws 協議請求與伺服器長連線,伺服器接受了,令人喜悅的 websocket 長連線開始。
(6)雙方都連線上後,服務端先執行 WebSocketServer 類 的 open回撥方法
(7)客戶端因為open成功,執行回撥,websocket.send(“my_id|{$user_id}”); 被執行。
(8)服務端執行 WebSocketServer 類 的 message 回撥方法
(9)服務端檢查是否特定開頭,來判定使用者端到底想做什麼,my_id開頭表示使用者自我介紹身份。
(10)查使用者id是否存在,如果存在,儲存到swoole_table, 且向使用者推送特定格式的訊息。歡迎您,某某某。
(11)客戶端的 onmessage 執行,顯示在對話方塊中,歡迎您,某某某。
(12)假設使用者離開,關閉瀏覽器的視窗,則服務端的close回撥被執行。使用者資訊從swoole_table中刪除

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章