swoole 的練習 demo(4)- 初具聊天室功能

yyy123456發表於2022-08-31

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

git倉庫和使用步驟

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

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

另外,前面3版其實有一些js錯誤,一併修復了。

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

需求

6、需要能正常聊天。就是別人說話,其他每個在聊天室的人都能看到這個人說的話。
7、如果某人上線,他自己看到 歡迎您,某某某,其他人看到,某某某 上線了。
8、如果某人下線。其他人看到,某某某 下線了。

測試網址

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

主要程式碼

客戶端主要程式碼

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

function activate_chat_js() {

 const me = 1; 
 const chatter = 0;

 // 這是每個人自己發訊息。子程式1。
 function update_chatWindow(incoming_message, from) {
   if (from === 1) { 
     var msg_html = my_message_html(incoming_message);
   } else if (from === 0) { 
     var msg_html = chatter_message_html(incoming_message); 
   } 
   $(".chat-window").append(msg_html);
 } 

 // 這是自己發訊息。子程式2。
 function my_message_html(incoming_message) {
   var newMessage = '<div class="me-wrapper">' + '<div class="me-message container">' + incoming_message + '</div>' + '<div class="me-avatar">' + '我自己' +
 '</div></div>'; 
   return newMessage; 
 }

 // click btn to send message 
 // 這也是每個人自己發訊息。最開始點選。
 $("#btn-send-message").click(function () {
   var user_input_area = $("#user-input-value"); 
   if (user_input_area.text().length == 0) {

   } else { 
     var message=user_input_area.text();
     update_chatWindow(user_input_area.text(), 1);
     user_input_area.text("");
     $('.chat-window').scrollTop($('.chat-window')[0].scrollHeight);
     // v4.0修改,把訊息給伺服器。
     window.websocket.send("my_message|{$user_id}|"+ message);
   } 
 });

 // ctrl+enter send message 
 // 這也是每個人自己發訊息,用Enter鍵。
 $('#user-input-value').keydown(function (e) { 
   if ((event.keyCode == 10 || event.keyCode == 13)) { 
     $('#btn-send-message').trigger("click"); 
     event.cancelBubble = true; 
     event.preventDefault();
   } 
 }); 
}

// 這是顯示在中間的系統訊息。由伺服器推送。
function system_chatWindow(incoming_message) {
   var msg_html = '<div class="system-message">' + incoming_message + '</div>' ;          
   $(".chat-window").append(msg_html);
   $('.chat-window').scrollTop($('.chat-window')[0].scrollHeight);
}

 // v4.0 ,這是被onmessage函式呼叫的方法,
// 這是其他人發的訊息,廣播到每個機器。
function update_chatWindow2(incoming_message, from) {
   var msg_html = '<div class="current-chatter-wrapper">' + '<div class="chatter-avatar">' + from + '</div>' + '<div class="chatter-message container">' + incoming_message + '</div>' + '</div>';     
   $(".chat-window").append(msg_html);
   $('.chat-window').scrollTop($('.chat-window')[0].scrollHeight);
}
window.onload = function(){  activate_chat_js();
  var wsServer = 'ws://{$JS_IP}:9501';
  var websocket = new WebSocket(wsServer); 
  window.websocket = websocket;    
  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);
      } 
      // v4.0修改。其他人接受某人的訊息廣播。
      if (user.type=='my_message'){
          update_chatWindow2(user.message, user.from_user_name );
      }     
  };

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

服務端主要程式碼

public function message(\swoole_websocket_server $server, \swoole_websocket_frame $frame)
{

    $data = $frame->data;
    echo "有訊息:".$data."\n";
    //這裡使用者發來的資訊已經分型別了。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) {
            //如果使用者不存在,則我直接關閉連線。
            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);
        echo "\n";
        // 放入記憶體表
        $this->table->set($frame->fd, $user);

        //給本人看 歡迎您。
        $server->push($frame->fd, json_encode([
           'type' => 'my_id',
           'message' => '歡迎您,' . $user_name,
        ]));
        // 通知其他人 某某 上線了。
        foreach ($this->table as $row) {
            if ($row['fd'] != $frame->fd) {
                $server->push($row['fd'], json_encode([
                   'type' => 'my_id',
                   'message' => $user_name . ' 上線了',
                ]));
            }
        } 
    } elseif (preg_match('#^my_message#', $data)) {
        $user_id = explode("|", $data)[1];
        $message = explode("|", $data)[2];

        $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;
        }
        echo "有人發訊息,內容:" . $message;
        echo "\n";

        // 通知其他人 ,進行廣播
        foreach ($this->table as $row) {
              if ($row['fd'] != $frame->fd) {
                  echo "推送訊息:" . $message."給fd:" . $row['fd'] ;
                  $server->push($row['fd'], json_encode([
                     'type' => 'my_message',
                     'message' => $message,
                     'from_user_name' => $user_name,
                  ]));
              }
        } 
    }
}

/**
 * 客戶端關閉的時候
  *
 * @param \swoole_websocket_server $server
 * @param int $fd
 */
 public function close(\swoole_websocket_server $server, int $fd)
 {
    $user = $this->table->get($fd);
    $user_name = $user['user_name'];

    echo "有人下線,資料:" . json_encode($user, JSON_UNESCAPED_UNICODE);

    $this->table->del($fd);
    // 通知其他人 某某 下線了。
    foreach ($this->table as $row) {
        $server->push($row['fd'], json_encode([
           'type' => 'my_id',
           'message' => $user_name . ' 下線了',
        ]));
     }
}

WebSocketServer.php 是核心檔案。

程式碼說明

這一版的功能總算象個樣子了!
swoole挺好用的。

效果示例

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

相關文章