安装
composer require topthink/think-worker
修改配置
config/gateway_worker.php
~~~
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// +----------------------------------------------------------------------
// | Workerman设置 仅对 php think worker:gateway 指令有效
// +----------------------------------------------------------------------
return [
// 扩展自身需要的配置
'protocol' => 'websocket', // 协议 支持 tcp udp unix http websocket text
'host' => '0.0.0.0', // 监听地址
'port' => 9501, // 监听端口 #修改
'socket' => '', // 完整监听地址
'ssl' => true,//#修改
'transport' => 'ssl',//#修改
'context' => [
"ssl" => [
'local_cert' => '/www/server/panel/vhost/cert/www.baidu.com.cn/fullchain.pem',//#修改
'local_pk' => '/www/server/panel/vhost/cert/www.baidu.com.cn/privkey.pem',//#修改
'verify_peer' => false,//#修改
],
], // socket 上下文选项
'register_deploy' => true, // 是否需要部署register
'businessWorker_deploy' => true, // 是否需要部署businessWorker
'gateway_deploy' => true, // 是否需要部署gateway
// Register配置
'registerAddress' => '127.0.0.1:1237',//#修改
// Gateway配置
'name' => 'thinkphp',
'count' => 1,
'lanIp' => '127.0.0.1',
'startPort' => 2000,
'daemonize' => false,
'pingInterval' => 30,
'pingNotResponseLimit' => 0,
'pingData' => '{"type":"ping"}',
// BusinsessWorker配置
'businessWorker' => [
'name' => 'BusinessWorker',
'count' => 1,
'eventHandler' => 'app\webscoket\Events',//#修改
],
];
~~~
新建事件处理逻辑类
app/webscoket/Events.php
<?php
/**
* Created by PhpStorm.
* User: Pasa吴
* Date: 2023/2/20
* Time: 14:21
*/
namespace app\webscoket;
use app\service\SearchUserService;
use app\service\UserService;
use GatewayWorker\Lib\Gateway;
use Workerman\Worker;
use think\worker\Application;
class Events
{
/**
* onWorkerStart 事件回调
* 当businessWorker进程启动时触发。每个进程生命周期内都只会触发一次
* @access public
* @param \Workerman\Worker $businessWorker
* @return void
*/
public static function onWorkerStart(Worker $businessWorker)
{
$app = new Application;
$app->initialize();
}
/**
* onConnect 事件回调
* 当客户端连接上gateway进程时(TCP三次握手完毕时)触发
* @access public
* @param int $client_id
* @return void
*/
public static function onConnect($client_id)
{
xdebug(compact('client_id'), 'onConnect', 'workerman');
Gateway::sendToCurrentClient(json_encode(['type' => 'notice', 'mes' => $client_id . ':连接成功'], JSON_UNESCAPED_UNICODE));
}
/**
* onWebSocketConnect 事件回调
* 当客户端连接上gateway完成websocket握手时触发
* @param integer $client_id 断开连接的客户端client_id
* @param mixed $data
* @return void
*/
public static function onWebSocketConnect($client_id, $data)
{
}
/**
* onMessage 事件回调
* 当客户端发来数据(Gateway进程收到数据)后触发
* @access public
* @param int $client_id
* @param mixed $data
* @return void
*/
public static function onMessage($client_id, $data)
{
xdebug(compact('client_id', 'data'), 'onMessage', 'workerman');
$param = json_decode($data);
if (isset($param->type) && $param->type) {
switch ($param->type) {
//登录
case 'login':
if (isset($param->token) && $param->token) {
//解析token
/* @var UserService $UserService */
$UserService = app(UserService::class);
$uid = $UserService->parseToken($param->token);
if ($uid) {
//绑定UID
Gateway::bindUid($client_id, $uid);
Gateway::sendToUid($uid, json_encode(['type' => 'notice', 'mes' => '登录成功UID:' . $uid], JSON_UNESCAPED_UNICODE));
return;
}
Gateway::sendToCurrentClient(json_encode(['type' => 'error', 'mes' => 'wss:token无效'], JSON_UNESCAPED_UNICODE));
}
break;
//加入房间
case 'join':
if (isset($param->room) && $param->room) {
//加入房间
Gateway::joinGroup($client_id, $param->room);
//获取UID
$uid = Gateway::getUidByClientId($client_id);
//向房间所有人发消息
Gateway::sendToGroup($param->room, json_encode(['type' => 'notice', 'mes' => "加入{$param->room}房间UID:" . $uid], JSON_UNESCAPED_UNICODE));
return;
}
Gateway::sendToCurrentClient(json_encode(['type' => 'error', 'mes' => 'wss:加入房间失败'], JSON_UNESCAPED_UNICODE));
break;
//离开房间
case 'leave':
if (isset($param->room) && $param->room) {
//将 client_id 离开组
Gateway::leaveGroup($client_id, $param->room);
//获取UID
$uid = Gateway::getUidByClientId($client_id);
//向房间所有人发消息
Gateway::sendToGroup($param->room, json_encode(['type' => 'notice', 'mes' => "离开{$param->room}房间UID:" . $uid], JSON_UNESCAPED_UNICODE));
return;
}
Gateway::sendToCurrentClient(json_encode(['type' => 'error', 'mes' => 'wss:离开房间失败'], JSON_UNESCAPED_UNICODE));
break;
//正在输入
case 'entering':
if (isset($param->room) && $param->room) {
//获取UID
$uid = Gateway::getUidByClientId($client_id);
$list = Gateway::getUidListByGroup($param->room);
foreach ($list as $key => $value) {
if ($value == $uid) {
continue;
}
//除了本人往房间所有发送消息
Gateway::sendToUid($value, json_encode(['type' => 'entering', 'mes' => '正在输入UID:' . $uid], JSON_UNESCAPED_UNICODE));
}
return;
}
Gateway::sendToCurrentClient(json_encode(['type' => 'error', 'mes' => 'wss:正在输入失败'], JSON_UNESCAPED_UNICODE));
break;
//输入结束
case 'enter':
if (isset($param->room) && $param->room) {
//获取UID
$uid = Gateway::getUidByClientId($client_id);
$list = Gateway::getUidListByGroup($param->room);
foreach ($list as $key => $value) {
if ($value == $uid) {
continue;
}
//除了本人往房间所有发送消息
Gateway::sendToUid($value, json_encode(['type' => 'enter', 'mes' => '输入结束UID:' . $uid], JSON_UNESCAPED_UNICODE));
}
return;
}
Gateway::sendToCurrentClient(json_encode(['type' => 'error', 'mes' => 'wss:停止输入失败'], JSON_UNESCAPED_UNICODE));
break;
//消息
case 'mes':
if (isset($param->room) && $param->room) {
//获取UID
$uid = Gateway::getUidByClientId($client_id);
Gateway::sendToGroup($param->room, json_encode(['type' => 'mes', 'mes' => '发送消息UID:' . $uid], JSON_UNESCAPED_UNICODE));
Gateway::sendToGroup($param->room, json_encode(['type' => 'mes', 'mes' => $param->message], JSON_UNESCAPED_UNICODE));
return;
}
Gateway::sendToCurrentClient(json_encode(['type' => 'error', 'mes' => 'wss:发送消息失败'], JSON_UNESCAPED_UNICODE));
break;
}
}
}
/**
* onClose 事件回调 当用户断开连接时触发的方法
* @param integer $client_id 断开连接的客户端client_id
* @return void
*/
public static function onClose($client_id)
{
xdebug(compact('client_id'), 'onClose', 'workerman');
GateWay::sendToAll(json_encode(['type' => 'close']));
}
/**
* onWorkerStop 事件回调
* 当businessWorker进程退出时触发。每个进程生命周期内都只会触发一次。
* @param \Workerman\Worker $businessWorker
* @return void
*/
public static function onWorkerStop(Worker $businessWorker)
{
echo "WorkerStop\n";
}
}
前端代码
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<button onclick="login()">登录</button>
<button onclick="join()">加入房间</button>
<button onclick="leave()">离开房间</button>
<button onclick="entering()">正在输入</button>
<button onclick="enter()">停止输入</button>
<input type="text" id="message">
<button onclick="send()">发送</button>
<div id="notice">通知<div>
<script>
var ws = new WebSocket("wss://www.baidu.cn:9501");
ws.onopen = function(){
console.log('连接成功');
}
//登录
function login(){
var token = prompt('请输入token');
var param = {type:'login', token:token}
ws.send(JSON.stringify(param));
}
//加入房间
function join()
{
var room = prompt('请输入房间号');
var param = {type:'join', room:room}
ws.send(JSON.stringify(param));
}
//离开房间
function leave()
{
var room = prompt('请输入房间号');
var param = {type:'leave', room:room}
ws.send(JSON.stringify(param));
}
//正在输入
function entering()
{
var room = prompt('请输入房间号');
var param = {type:'entering', room:room}
ws.send(JSON.stringify(param));
}
//输入结束
function enter()
{
var room = prompt('请输入房间号');
var param = {type:'enter', room:room}
ws.send(JSON.stringify(param));
}
//发送消息
function send()
{
var message = document.getElementById('message').value;
var room = prompt('请输入接收消息的房间号')
var param = {type:'mes', room:room, message:message}
ws.send(JSON.stringify(param));
}
//接受服务端消息
ws.onmessage = function(data){
data = data.data;
console.log(data,'onmessage');
data = JSON.parse(data);
switch (data.type){
//调试用的,不需要理会
case 'notice':
console.log(data.mes,'notice');
document.getElementById('notice').innerHTML+='<br>';
document.getElementById('notice').innerHTML+=data.mes;
break;
//正在输入,显示正在输入
case 'entering':
console.log(data.mes,'entering');
document.getElementById('notice').innerHTML+='<br>';
document.getElementById('notice').innerHTML+=data.mes;
break;
//输入结束,显示老师名称
case 'enter':
console.log(data.mes,'enter');
document.getElementById('notice').innerHTML+='<br>';
document.getElementById('notice').innerHTML+=data.mes;
break;
//websocket错误,弹出内容提示
case 'error':
console.log(data.mes,'enter');
document.getElementById('notice').innerHTML+='<br>';
document.getElementById('notice').innerHTML+=data.mes;
break;
//发送消息,调用聊天内容列表接口
case 'mes':
console.log(data.mes,'enter');
document.getElementById('notice').innerHTML+='<br>';
document.getElementById('notice').innerHTML+=data.mes;
break;
default:
}
console.log(data.type,'onmessage2');
}
ws.onclose = function(){
console.log('连接断开');
}
</script>
</body>
</html>
在服务器根目录下启动命令
php think worker:gateway
php think worker:gateway status
php think worker:gateway start
php think worker:gateway -d
php think worker:gateway stop
php think worker:gateway reload