一直不能下決心好好學習,仔細研究一下,決定用盡量降低難度曲線的方法,從易到難,一步一步的學習,所以整了個demo專案。
git倉庫和使用步驟
確保能看到swoole,在 php -m 命令中
php -m
git clone https://github.com/lang123789/swoole_demo.git
然後設定了標籤,本文對應 v6.0
cd swoole_demo
git checkout v6.0
## 安裝類庫,生成 autoload.php 檔案
composer install
## 啟動 websocket 服務
php src/WebSocket/run.php
需求
10、需要把使用者的聊天訊息儲存到資料庫。
11、需要校驗使用者id是否正確,是否在表中存在
設計
建表 和插入假資料。
這裡有兩張表,1使用者表,2聊天記錄表。
CREATE TABLE users (
id int(11) NOT NULL AUTO_INCREMENT,
user_name varchar(255) NOT NULL DEFAULT '' comment '使用者名稱稱',
email varchar(255) NOT NULL DEFAULT '' comment 'email',
created_at timestamp null default current_timestamp,
PRIMARY KEY (`id`)
) ENGINE=InnoDB ;
CREATE TABLE messages (
id int(11) NOT NULL AUTO_INCREMENT,
user_id int not null default 0 comment '使用者id',
content varchar(3000) NOT NULL DEFAULT '' comment '聊天內容',
created_at timestamp null default current_timestamp,
PRIMARY KEY (`id`),
index user_id(user_id)
) ENGINE=InnoDB ;
insert into users(id,user_name)values (1,'管理員');
insert into users(id,user_name)values (2,'使用者2');
insert into users(id,user_name)values (3,'使用者3');
資料庫外掛選型
各方比較之後,選擇用 Mix Database
有一說一,是真好用。
官方地址:openmix.org/mix-php/docs/3.0/#/zh-...
安裝命令:
composer require mix/database
客戶端程式碼改變 index.php
//假定這是資料庫的查詢結果
$config = require __DIR__ .'/config/mysql.php';
$db = new \Mix\Database\Database('mysql:host='. $config['host'] .';port='. $config['port']
.';charset='. $config['charset'] .';dbname='.$config['db_name'], $config['username'], $config['password']);
$datas = $db->table('users')->where('id = ?', $user_id)->first();
if (empty($datas)){
die('非法使用者');
}
服務端程式碼改變
// 資料庫連線池
private function create_mysql_pool()
{
$maxOpen = 50; // 最大開啟連線數
$maxIdle = 20; // 最大閒置連線數
$maxLifetime = 3600; // 連線的最長生命週期
$waitTimeout = 0.0; // 從池獲取連線等待的時間, 0為一直等待
$config = $this->mysql_config['mysql']; // 讀取配置檔案。
$this->db = new \Mix\Database\Database('mysql:host='. $config['host'] .';port='. $config['port']
.';charset='. $config['charset'] .';dbname='.$config['db_name'], $config['username'], $config['password']);
$this->db->startPool($maxOpen, $maxIdle, $maxLifetime, $waitTimeout);
\Swoole\Runtime::enableCoroutine(); // 必須放到最後,防止觸發協程排程導致異常
}
public function message(\swoole_websocket_server $server, \swoole_websocket_frame $frame)
{
$data = $frame->data;
//echo "有訊息:" . $data . "\n";
//這裡使用者發來的資訊已經分型別了。my_id開頭,說明是系統自動從客戶端傳送的資訊,用於識別身份。
if (preg_match('#^ping#', $data)) { // 心跳
// echo "心跳來了 " . date("Y-m-d H:i:s") . "\n"; $server->push($frame->fd, 'pong');// 返回一個訊息,過會他會再次傳來。
} elseif (preg_match('#^my_id#', $data)) { // 使用者上線
$user_id = explode("|", $data)[1];
$datas = $this->db->table('users')->where('id = ?', $user_id)->first();
if (!$datas) {
//如果使用者不存在,則w我直接關閉連線。
echo "非法連線。使用者id:" . $user_id;
$server->close($frame->fd);
return;
}
$user_name = $datas->user_name;
$user = [
'fd' => $frame->fd,
'user_name' => $user_name,
'user_id' => strval($user_id),
];
echo "有個人剛上線,資料:" . json_encode($user, JSON_UNESCAPED_UNICODE);
echo "連線池當前狀態:".json_encode( $this->db->poolStats() );
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];
$datas = $this->db->table('users')->where('id = ?', $user_id)->first();
if (!$datas) {
//如果使用者不存在,則w我直接關閉連線。
echo "非法連線。使用者id:" . $user_id;
$server->close($frame->fd);
return;
}
$user_name = $datas->user_name;
$data = [
'user_id' => $user_id,
'content' => $message,
];
$this->db->insert('messages', $data); // 插入資料庫
echo "有人發訊息,內容:" . $message;
echo "連線池當前狀態:".json_encode( $this->db->poolStats() );
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,
]));
}
}
}
}
程式碼說明
1、這次的程式碼變動其實比較少,去除了最開始的假的使用者表,使用了真實的使用者表。
2、也使用了真實的訊息表儲存使用者的訊息,上面的程式碼中能找到 “插入資料庫” 的註釋。
3、mix 這個類庫真好用,程式碼特別少特別精簡。但說明一下,我沒有在真實專案中用過 mix。只是這個 demo 中用的。
4、真實的專案中,其實客服是固定的,應該不需要廣播,資料庫使用者表中,新增是否客服的欄位,都不難設計的,程式碼也不難寫。
5、也可以有這樣的需求,程式碼對方是否已讀,也可以加欄位實現。
6、也可以有這樣的需求,使用者表加入是否線上的欄位。
7、也可以有這樣的需求,客服的前端頁面會不同,因為客服與很多人對話,他得看的方便,所以得是一個人一個視窗這樣。但這些都需要前端程式設計師的配合。
截圖
本作品採用《CC 協議》,轉載必須註明作者和本文連結