這次要開發聊天系統 , 需要用到WebSocket 我使用的是workerman+gateway,為了方便后面再用,做個簡單記錄
首先要特別注意的是,端口要開放,如果端口未開放,會出現連接時握手失敗的情況,這里我用的商品是 801 和 802
1、安裝workerman和gateway
composer require topthink/think-worker
composer require workerman/gatewayclient
2、添加server.php文件,后成需要在CLI模式下運行
#!/usr/bin/env php
<?php
ini_set('display_errors', 'on');
if(strpos(strtolower(PHP_OS), 'win') === 0)
{
exit("start.php not support windows.\n");
}
// 檢查擴展
if(!extension_loaded('pcntl'))
{
exit("Please install pcntl extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
}
if(!extension_loaded('posix'))
{
exit("Please install posix extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
}
define('APP_PATH', __DIR__ . '/application/');
define('BIND_MODULE','push/Run');
// 加載框架引導文件
require __DIR__ . '/thinkphp/start.php';
3、創建push模塊新建兩個控制器Run.php Events.php
Start.php
<?php
namespace app\push\controller;
use Workerman\Worker;
use GatewayWorker\Register;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
class Run extends Worker{
/**
* 構造函數
* @access public
*/
public function __construct(){
//由於是手動添加,因此需要注冊命名空間,方便自動加載,具體代碼路徑以實際情況為准
\think\Loader::addNamespace([
'Workerman' => VENDOR_PATH . 'Workerman/workerman',
'GatewayWorker' =>VENDOR_PATH . 'Workerman/gateway-worker/src',
]);
//初始化各個GatewayWorker
//初始化register
new Register('text://0.0.0.0:801');
//初始化 bussinessWorker 進程
$worker = new BusinessWorker();
$worker->name = 'WebIMBusinessWorker';
$worker->count = 4;
$worker->registerAddress = '127.0.0.1:801';
//設置處理業務的類,此處制定Events的命名空間
$worker->eventHandler = '\app\push\controller\Events';
// 初始化 gateway 進程
$gateway = new Gateway("websocket://0.0.0.0:802");
$gateway->name = 'WebIMGateway';
$gateway->count = 4;
$gateway->lanIp = '127.0.0.1';
$gateway->startPort = 2900;
$gateway->registerAddress = '127.0.0.1:801';
//運行所有Worker;
Worker::runAll();
}
}
Events.php
<?php
namespace app\push\controller;
use GatewayWorker\Lib\Gateway;
/**
* 主邏輯
* 主要是處理 onConnect onMessage onClose 三個方法
* onConnect 和 onClose 如果不需要可以不用實現並刪除
*/
class Events{
/**
* 當客戶端發來消息時觸發
* @param int $client_id 連接id
* @param mixed $data 具體消息
*/
public static function onMessage($client_id, $data){
$message = json_decode($data, true);
$message_type = $message['type'];
switch($message_type) {
case 'init':
// uid
$uid = $message['id'];
// 設置session
$_SESSION = [
'username' => $message['username'],
'avatar' => isset($message['avatar'])?$message['avatar']:'',
'id' => $uid,
'sign' => $message['sign'],
'type'=> $type,
];
// 將當前鏈接與uid綁定
Gateway::bindUid($client_id, $uid);
// 通知當前客戶端初始化
$init_message = array(
'message_type' => 'init',
'id' => $uid,
);
Gateway::sendToClient($client_id, json_encode($init_message));
return;
break;
case 'chatMessage':
// 聊天消息
$type = $message['data']['to']['type'];
$to_id = $message['data']['to']['id'];
$uid = $_SESSION['id'];
$chat_message = [
'message_type' => 'chatMessage',
'data' => [
'username' => $_SESSION['username'],
'avatar' => $_SESSION['avatar'],
'id' => $type === 'friend' ? $uid : $to_id,
'type' => $type,
'content' => htmlspecialchars($message['data']['mine']['content']),
'timestamp'=> time()*1000,
]
];
return Gateway::sendToUid($to_id, json_encode($chat_message));
break;
case 'hide':
case 'online':
$status_message = [
'message_type' => $message_type,
'id' => $_SESSION['id'],
];
$_SESSION['online'] = $message_type;
Gateway::sendToAll(json_encode($status_message));
return;
break;
case 'ping':
return;
default:
echo "unknown message $data" . PHP_EOL;
}
}
/**
* 當客戶端連接時觸發
* 如果業務不需此回調可以刪除onConnect
*
* @param int $client_id 連接id
*/
public static function onConnect($client_id)
{
var_dump($client_id);
Gateway::sendToClient($client_id,json_encode(['status'=>"success",'msg'=>"連接成功"]));
Gateway::sendToAll("連接成功");
}
/**
* 當連接斷開時觸發的回調函數
* @param $connection
*/
public static function onClose($client_id){
$logout_message = [
'message_type' => 'logout',
'id' => $_SESSION['id']
];
Gateway::sendToAll(json_encode($logout_message));
}
/**
* 當客戶端的連接上發生錯誤時觸發
* @param $connection
* @param $code
* @param $msg
*/
public static function onError($client_id, $code, $msg)
{
echo "error $code $msg\n";
}
/**
* 每個進程啟動
* @param $worker
*/
public static function onWorkerStart($worker)
{
}
}
然后執行命令開啟進程
php server.php start
前端示例:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
<script>
ws = new WebSocket("ws:0.0.0.0:802"); //填你線上服務端地址
// 服務端主動推送消息時會觸發這里的onmessage
ws.onopen = function(){
console.info("與服務端連接成功");
ws.send('test msg\n');//相當於發送一個初始化信息
console.info("向服務端發送心跳包字符串");
};
ws.onmessage = function(e){
// json數據轉換成js對象
var data = eval("("+e.data+")");
console.log(data.msg);
};
ws.onclose = function(e){
console.log(e);
};
</script>
</body>
</html>
