一直不能下決心好好學習,仔細研究一下,決定用盡量降低難度曲線的方法,從易到難,一步一步的學習,所以整了個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 協議》,轉載必須註明作者和本文連結