php搭建websocket

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.更新宝塔文件
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值