workerman文档:https://www.workerman.net/doc/gateway-worker/unbind-uid.html
1.项目终端执行命令:composer require topthink/think-worker 2.0.x
2.config多出三个配置文件:
3.当使用php think worker:gateway命令时,提示不支持Windows。
4.打包项目为zip格式
5.打包数据库
6.阿里云创建记录
7.宝塔面板新增站点
8.访问测试,查看是否成功(下图为成功)
9.上传源码到站点
10.解压源码并删除源码压缩包
11.把网站目录改为public
12.伪静态选择thinkphp
13.导入数据库
14.修改站点配置文件database
15.将database.php配置文件中的数据库名、用户名、密码修改成这个
16.访问接口(下图说明文件跑起来了,没啥问题)
17.打开ceshi2站点终端
18.终端打开websocket
注意看:2348是websocket端口号
19.Ctrl+c停止服务
20.配置gateway_worker.php文件
21.放行端口号
22.运行worker:gateway
23.搜索webSocket在线测试
24.查看终端会打印出客户端的信息
25.用户绑定客户端api
创建自定义events文件:\app\lib\socket\Events.php 用于处理gatewayworker
代码如下:
<?php
namespace app\lib\socket;
use GatewayWorker\Lib\Gateway;
use Workerman\Worker;
use think\facade\Cache;
/**
* Worker 命令行服务类
*/
class Events
{
/**
* onConnect 事件回调
* 当客户端连接上gateway进程时(TCP三次握手完毕时)触发
*/
public static function onConnect($client_id)
{
// $data = [ 'type'=>'client_id', 'data'=>$client_id ];
// Gateway::sendToCurrentClient(json_encode($data));
}
/**
* onWebSocketConnect 事件回调
* 当客户端连接上gateway完成websocket握手时触发
*/
public static function onWebSocketConnect($client_id, $data)
{
// Gateway::sendToCurrentClient(json_encode([ 'type'=>'client_id', 'data'=>$client_id ]));
// var_export($client_id);
}
/**
* onMessage 事件回调
* 当客户端发来数据(Gateway进程收到数据)后触发
*/
public static function onMessage($client_id, $data)
{
//var_export($data);
// 验证当前客户端是否已经绑定
if (Gateway::getUidByClientId($client_id)) return;
$data = json_decode($data,true);
// 非法参数
if (!is_array($data) || !array_key_exists('type',$data) || !array_key_exists('token',$data) || $data['type'] !== 'bind' || empty($data['token'])) return;
$user = Cache::get($data['token']);
if (!$user) return Gateway::sendToCurrentClient(json_encode(['type'=>'bind','msg'=>'非法token,禁止操作','status'=>false]));
// 获取用户id
$userId = array_key_exists('type',$user) ? $user['user_id'] : $user['id'];
// 验证第三方是否绑定手机
if ($userId < 1) return Gateway::sendToCurrentClient(json_encode(['type'=>'bind','msg'=>'请先绑定手机','status'=>false]));
$User = \app\common\model\User::find($userId);
// 验证用户是否绑定手机
if (!$User->phone) return Gateway::sendToCurrentClient(json_encode(['type'=>'bind','msg'=>'请先绑定手机','status'=>false]));
// 验证用户状态
if ($User->status == 0) return Gateway::sendToCurrentClient(json_encode(['type'=>'bind','msg'=>'当前用户被禁用','status'=>false]));
// 绑定
Gateway::bindUid($client_id,$userId);
return Gateway::sendToCurrentClient(json_encode(['type'=>'bind','msg'=>'绑定成功','status'=>true]));
}
/**
* onClose 事件回调 当用户断开连接时触发的方法
*/
public static function onClose($client_id)
{
//GateWay::sendToAll("client[$client_id] logout\n");
}
/**
* onWorkerStop 事件回调
* 当businessWorker进程退出时触发。每个进程生命周期内都只会触发一次。
*/
public static function onWorkerStop(Worker $businessWorker)
{
echo "WorkerStop\n";
}
}
25.配置gateway_worker.php文件
26.上传gateway_worker.php
27.新建socket目录
28.上传Events.php
29.重启gateway以修改配置文件
30.修改apiurl为部署到服务器上的地址(我知道各位这步可能不大理解,可以去看我前面发的博客中的postman)
31.创建聊天api相关的控制器与验证器(并删除文件中多余内容)
命令:php think make:controller api/v1/Chat
注意Chat首字母大写
php think make:validate ChatValidate
32.chat相关文件中写入相应内容
chat.php中内容:
<?php
namespace app\api\controller\v1;
use think\Controller;
use think\Request;
use app\common\controller\BaseController;
use app\common\validate\ChatValidate;
use GatewayWorker\Lib\Gateway;
use think\facade\Cache;
class chat extends BaseController
{
// 初始化registerAddress
public function __construct(){
Gateway::$registerAddress = config('gateway_worker.registerAddress');
}
// 发送信息
public function send(Request $request){
// 1. 验证数据是否合法
(new ChatValidate)->goCheck('send');
// 2. 组织数据
$data = $this->resdata($request);
$to_id = $request->to_id;
// 3. 验证对方用户是否在线
if (Gateway::isUidOnline($to_id)) {
// 直接发送
Gateway::sendToUid($to_id,json_encode($data));
// 写入数据库
// 返回发送成功
return self::showResCodeWithOutData('ok');
}
// 不在线,写入消息队列
// 获取之前消息
$Cache = Cache::get('userchat_'.$to_id);
if (!$Cache || !is_array($Cache)) $Cache = [];
$Cache[] = $data;
// 写入数据库
// 写入消息队列(含id)
Cache::set('userchat_'.$to_id,$Cache);
return self::showResCodeWithOutData('ok',200);
}
// 组织数据
public function resdata($request){
return [
'to_id'=>$request->to_id,
'from_id'=>$request->userId,
'from_userpic'=>$request->from_userpic,
'type'=>$request->type,
'data'=>$request->data,
'time'=>time()
];
}
}
chatValidate.php
<?php
namespace app\common\validate;
use think\Validate;
class ChatValidate extends BaseValidate
{
/**
* 定义验证规则
* 格式:'字段名' => ['规则1','规则2'...]
*
* @var array
*/
protected $rule = [
'to_id'=>'require|isUserExist',
'from_userpic'=>'require',
'type'=>'require',
'data'=>'require',
];
/**
* 定义错误信息
* 格式:'字段名.规则名' => '错误信息'
*
* @var array
*/
protected $message = [];
protected $scene = [
'send'=>['to_id','from_userpic','type','data']
];
}
在route.php尾部中加入
// socket 部分
Route::group('api/:version/',function(){
// 发送信息
Route::post('chat/send','api/:version.Chat/send');
})->middleware(['ApiUserAuth','ApiUserBindPhone','ApiUserStatus']);
33.获取离线消息api
把方法加入到控制器层:application\api\v1\Chat.php
// 接收未接收信息
public function get(Request $request){
// 判断当前用户是否在线
if (!Gateway::isUidOnline($request->userId)) return;
// 获取并清除所有未接收信息
$Cache = Cache::pull('userchat_'.$request->userId);
if (!$Cache || !is_array($Cache)) return;
// 开始推送
return self::showResCode('ok',$Cache);
}
编辑route.php,加入红框中的代码。
34.上传更新文件
35.分布式即时通讯框架GatewayWorker下载——下载到项目根目录的extend中
框架下载:看我上传的一个GatewayWorker资源(资源是免费的,我csdn等级为0,只能上传免费的,大家不下白不下)。https://download.csdn.net/download/m0_54136420/89035318
36.将vendor/workerman中的所有文件删除
37.修改这四个php文件
Events.php
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* 用于检测业务代码死循环或者长时间阻塞等问题
* 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
* 然后观察一段时间workerman.log看是否有process_timeout异常
*/
//declare(ticks=1);
use \GatewayWorker\Lib\Gateway;
/**
* 主逻辑
* 主要是处理 onConnect onMessage onClose 三个方法
* onConnect 和 onClose 如果不需要可以不用实现并删除
*/
class Events
{
/**
* 当客户端连接时触发
* 如果业务不需此回调可以删除onConnect
*
* @param int $client_id 连接id
*/
public static function onConnect($client_id)
{
// 发送当前客户端id给用户
return Gateway::sendToCurrentClient(json_encode(['type'=>'bind','data'=>$client_id]));
}
/**
* 当客户端发来消息时触发
* @param int $client_id 连接id
* @param mixed $message 具体消息
*/
public static function onMessage($client_id, $data)
{
}
/**
* 当用户断开连接时触发
* @param int $client_id 连接id
*/
public static function onClose($client_id)
{
}
}
start_businessworker.php
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';
// bussinessWorker 进程
$worker = new BusinessWorker();
// worker名称
$worker->name = 'YourAppBusinessWorker';
// bussinessWorker进程数量
$worker->count = 8;
// 服务注册地址
$worker->registerAddress = '127.0.0.1:1236';
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
start_gateway.php
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;
// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';
// gateway 进程,这里使用Text协议,可以用telnet测试
$gateway = new Gateway("websocket://0.0.0.0:23481");
// gateway名称,status方便查看
$gateway->name = 'YourAppGateway';
// gateway进程数
$gateway->count = 8;
// 本机ip,分布式部署时使用内网ip
$gateway->lanIp = '127.0.0.1';
// 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
// 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
$gateway->startPort = 2900;
// 服务注册地址
$gateway->registerAddress = '127.0.0.1:1236';
// 心跳间隔
$gateway->pingInterval = 30;
// 心跳数据
$gateway->pingData = '{"type":"ping"}';
/*
// 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
$gateway->onConnect = function($connection)
{
$connection->onWebSocketConnect = function($connection , $http_header)
{
// 可以在这里判断连接来源是否合法,不合法就关掉连接
// $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net')
{
$connection->close();
}
// onWebSocketConnect 里面$_GET $_SERVER是可用的
// var_dump($_GET, $_SERVER);
};
};
*/
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
start_register.php
<?php
/**
* This file is part of workerman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use \Workerman\Worker;
use \GatewayWorker\Register;
// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';
// register 必须是text协议
$register = new Register('text://0.0.0.0:1236');
// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
Worker::runAll();
}
38.在tp5框架中引入gatewayworker
在/public/index.php
38.宝塔面板文件更新
39.用新方式打开gateway
40.用户绑定上线
加在app/controller/v1/Chat.php中
// 绑定上线
public function bind(Request $request){
//{type:"bind",client_id:"7f0000010b5700000001"}';
// 验证当前用户是否绑定手机号,状态等信息,验证数据合法性
(new ChatValidate)->goCheck('bind');
$userId = $request->userId;
$client_id = $request->client_id;
// 验证client_id合法性
if (!Gateway::isOnline($client_id)) return TApiException('clientId不合法');
// 验证当前客户端是否已经绑定
if (Gateway::getUidByClientId($client_id)) return TApiException('已被绑定');
// 直接绑定
Gateway::bindUid($request->client_id,$userId);
// 返回成功
return self::showResCode('绑定成功',['type'=>'bind','status'=>true]);
}
修改ChatValidate.php文件
修改路由
41.更新宝塔文件