tp5 + workerman 实现聊天室

最近公司的项目需要做一个简单的聊天,项目用的框架是thinkphp 5,开发任务主要是实现买卖双方生成订单后,可以在线交流。

无意间在tp手册上看到 workerman socket包,然后就安装了试试。

实现的思路是,将订单的id作为房间号房间人员指定为订单双方的用户,数据库中将用户id与的客户端fd对应,用户进入时,登录时将用户id与客户端fd存入数据库,发送时根据用户fd发送给当前用户,断开连接时,将用户fd清除。

需要的资料:

workerman手册   http://doc.workerman.net/

下边先介绍下包的下载

1.

首先通过 composer 安装

composer require topthink/think-worker

如果需要在window下做服务端,还需要

composer require workerman/workerman-for-win

数据库操作包

composer require workerman/mysql
或者可以将tp5的DB类引进来,或者使用redis,本文只是将流程写下来,项目使用还是需要将代码优化

下载完成后会多出来这两个文件夹,然后我们将workerman 启动命令添加到tp5中

在下边的目录下创建Workerman.php文件

<?php
/**
 * Created by PhpStorm.
 * User: 827169570@qq.com
 * Date: 2019/3/13
 * Time: 20:25
 */
namespace app\common\command;
namespace app\common\command;

use app\workerman\Events;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use Workerman\Worker;

class Workerman extends Command
{
    protected function configure()
    {
        $this->setName('workerman')
            ->addArgument('action', Argument::OPTIONAL, "action  start|stop|restart")
            ->addArgument('type', Argument::OPTIONAL, "d -d")
            ->setDescription('workerman chat');
    }

    protected function execute(Input $input, Output $output)
    {
        global $argv;
        $action = trim($input->getArgument('action'));
        $type   = trim($input->getArgument('type')) ? '-d' : '';

        $argv[0] = 'chat';
        $argv[1] = $action;
        $argv[2] = $type ? '-d' : '';
        $this->start();
    }
    private function start()
    {
        $this->startGateWay();
        $this->startBusinessWorker();
        $this->startRegister();
        Worker::runAll();
    }

    private function startBusinessWorker()
    {
        $worker                  = new BusinessWorker();
        $worker->name            = 'BusinessWorker';
        $worker->count           = 1;
        $worker->registerAddress = '127.0.0.1:1236';
        $worker->eventHandler    = Events::class;
    }

    private function startGateWay()
    {
        $gateway = new Gateway("websocket://0.0.0.0:8282");
        $gateway->name                 = 'Gateway';
        $gateway->count                = 1;
        $gateway->lanIp                = '127.0.0.1';
        $gateway->startPort            = 2300;
        $gateway->pingInterval         = 30;
        $gateway->pingNotResponseLimit = 0;
        $gateway->pingData             = '{"type":"@heart@"}';
        $gateway->registerAddress      = '127.0.0.1:1236';
    }

    private function startRegister()
    {
        new Register('text://0.0.0.0:1236');
    }
}

然后将'app\common\command\Workerman', 加入到 command.php 文件里边

然后再创建个监听文件Events.php

<?php
/**
 * Created by PhpStorm.
 * User: 827169570@qq.com
 * Date: 2019/3/13
 * Time: 20:27
 */
namespace app\workerman;
use app\common\controller\Checking;
use GatewayWorker\Lib\Gateway;
class Events
{

    public static function onWorkerStart($businessWorker)
    {
        // 将db实例存储在全局变量中(也可以存储在某类的静态成员中)
        global $db;
        $db = new \Workerman\MySQL\Connection('数据库地址', '端口号', '账号', '密码', '表');
    }

    public static function onConnect($client_id)
    {
    }

    public static function onWebSocketConnect($client_id, $data)
    {
    }

    /**
     * 有消息时
     * @param int $client_id
     * @param mixed $message
     */
    public static function onMessage($client_id, $message)
    {
        // debug
        echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']}  client_id:$client_id session:".json_encode($_SESSION)." onMessage:".$message."\n";

        // 客户端传递的是json数据
        $message_data = json_decode($message, true);
        if(!$message_data)
        {
            return ;
        }

        // 根据类型执行不同的业务
        switch($message_data['type'])
        {
            // 客户端回应服务端的心跳
            case 'pong':
                return;
                //聊天记录分页
               // message格式: {type:page, room_id:xx, start:1} ,添加到客户端,广播给所有客户端xx进入聊天室
            case 'page':
                global $db;
                $offset=10*$message_data['start'];
                $log =$db->select('*')->where('room_id= :id')->bindValues(['id'=>$message_data['room_id']])->from('de_chat_log')->orderByDESC(['id'])->limit(10)->offset($offset)->query();
                //print_r($log);
                $log=array_reverse($log);
                $new_message = array(
                    'type'=>$message_data['type'],
                    'client_id'=>$client_id,
                    'room_id'=>$message_data['room_id'],
                    'content'=>json_encode($log),
                    'time'=>date('Y-m-d H:i:s'));
                Gateway::sendToCurrentClient(json_encode($new_message));
                break;
            // 客户端登录 message格式: {type:login, phone:xx, order_id:1} ,添加到客户端,广播给所有客户端xx进入聊天室
            case 'login':
                if(isset($message_data['order_id'])) {
                    #房间不存在 创建房间
                    global $db;
                    $order =$db->select('*')->where('id= :id')->bindValues(array('id'=>$message_data['order_id']))->from('de_kt_detail')->row();
                    #判断是否建过房间
                    $is_room=$db->select('*')->where('user_id= :user_id AND party_id= :party_id')->bindValues(array('user_id'=>$order['user_id'],'party_id'=>$order['buy_id']))->from('de_room')->row();
                    if (empty($is_room)){
                        $is_room=$db->select('*')->where('user_id= :user_id AND party_id= :party_id')->bindValues(array('user_id'=>$order['buy_id'],'party_id'=>$order['user_id']))->from('de_room')->row();
                        if (empty($is_room)){
                            #创建房间
                            $user_info=$db->select('mobile,id')->where('id= :id')->bindValues(array('id'=>$order['user_id']))->from('de_users')->row();
                            $party_info=$db->select('mobile,id')->where('id= :id')->bindValues(array('id'=>$order['buy_id']))->from('de_users')->row();
//                            if ($order['user_id']==$order['buy_id']){
//                                #创建房间异常
//                                $new_message=[
//                                    'status'=>203,
//                                    'message'=>'房间创建失败'
//                                ];
//                                return Gateway::sendToCurrentClient(json_encode($new_message));
//                            }
                            $room_id_db = $db->insert('de_room')->cols(array('user_id'=>$order['user_id'],'user_phone'=>$user_info['mobile'],'accept_phone'=>$party_info['mobile'],'party_id'=>$order['buy_id']))->query();
                            if ($room_id_db==''){
                                #创建房间异常
                                $new_message=[
                                    'status'=>201,
                                    'message'=>'房间创建失败'
                                ];
                                return Gateway::sendToCurrentClient(json_encode($new_message));
                            }
                            $is_room['id']=$room_id_db;
                        }

                    }
                    #查询当前用户id
                    $user=$db->select('mobile,id')->where('mobile= :phone')->bindValues(array('phone'=>$message_data['phone']))->from('de_users')->row();
                    $fd=$db->select('*')->where('id= :id')->bindValues(array('id'=>$is_room['id']))->from('de_room')->row();
                    // 判断是否有房间是否开启
                    //print_r($client_id);
                    if ($user['id']==$fd['user_id']){
                        $db->update('de_room')->cols(array('user_fd'=>$client_id))->where('id= :id')->bindValues(array('id'=>$is_room['id']))->query();
                    }
                    if ($user['id']==$fd['party_id']){
                        $db->update('de_room')->cols(array('party_fd'=>$client_id))->where('id= :id')->bindValues(array('id'=>$is_room['id']))->query();
                    }
                }else{
                    $new_message=[
                        'status'=>202,
                        'message'=>'参数异常'
                    ];
                    return Gateway::sendToCurrentClient(json_encode($new_message));
                }
                $log =$db->select('*')->where('room_id= :id')->bindValues(['id'=>$is_room['id']])->from('de_chat_log')->orderByDESC(['id'])->limit(10)->offset(0)->query();
                $log=array_reverse($log);
//                print_r($log);
                $new_message = array(
                    'type'=>$message_data['type'],
                    'client_id'=>$client_id,
                    'room_id'=>$is_room['id'],
                    'content'=>json_encode($log),
                    'time'=>date('Y-m-d H:i:s'));
                Gateway::sendToCurrentClient(json_encode($new_message));
                return;

            // 客户端发言 message: {type:say, to_client_id:xx, room_id:xx, content:xx}
            case 'say':
                // 通过全局变量获得db实例
                global $db;
                #判断是否是图文
                if (isset($message_data['is_image'])){
                    if ($message_data['is_image']==2){
                        $log['is_img']='2';
                    }else{
                        $log['is_img']='1';
                    }
                }
                #房间所有用户
                $fd=$db->select('*')->where('id= :id')->bindValues(array('id'=>$message_data['room_id']))->from('de_room')->row();
                if ($fd['user_fd']!=''&&$fd['party_fd']!=''){
                    #双方在线
                    #存储记录
                        if ($fd['user_fd']==$client_id){
                            $log['user_id']=$fd['user_id'];
                            $log['user_phone']=$fd['user_phone'];
                            $log['accept_id']=$fd['party_id'];
                            $log['accept_phone']=$fd['accept_phone'];
                            $to_client_id=$fd['party_fd'];
                        }else{
                            $log['user_id']=$fd['party_id'];
                            $log['user_phone']=$fd['accept_phone'];
                            $log['accept_id']=$fd['user_id'];
                            $log['accept_phone']=$fd['user_phone'];
                            $to_client_id=$fd['user_fd'];
                        }
                        $log['message']=$message_data['content'];
                        $log['room_id']=$message_data['room_id'];
                        $log['create_time']=date('Y-m-d H:i:s');
                        $log['is_read']='2';
//                    $new_message = [
//                        'type'=>'say',
//                        'from_client_id'=>$client_id,
//                        'from_client_name' =>'1',
//                        'to_client_id'=>$to_client_id,
//                        'content'=>nl2br(htmlspecialchars($log)),
//                        'time'=>date('Y-m-d H:i:s'),
//                    ];
                    $new_message = array(
                        'type'=>$message_data['type'],
                        'client_id'=>$client_id,
                        'room_id'=>$message_data['room_id'],
                        'content'=>json_encode($log),
                        'time'=>date('Y-m-d H:i:s')
                    );
                    $db->insert('de_chat_log')->cols($log)->query();
                    Gateway::sendToClient($to_client_id, json_encode($new_message));
                    return Gateway::sendToCurrentClient(json_encode($new_message));
                }else{
                    #接收方未在线
                    if ($fd['user_fd']==$client_id){
                        $log['user_id']=$fd['user_id'];
                        $log['user_phone']=$fd['user_phone'];
                        $log['accept_id']=$fd['party_id'];
                        $log['accept_phone']=$fd['accept_phone'];
                    }else{
                        $log['user_id']=$fd['party_id'];
                        $log['user_phone']=$fd['accept_phone'];
                        $log['accept_id']=$fd['user_id'];
                        $log['accept_phone']=$fd['user_phone'];
                    }
                    #查询接受方用户id
//                        print_r($accept_id);
                    $log['message']=$message_data['content'];
                    $log['room_id']=$message_data['room_id'];
                    $log['create_time']=date('Y-m-d H:i:s');
                    $log['is_read']='1';
                    print_r($log);
                    $db->insert('de_chat_log')->cols($log)->query();
                }
                $new_message = [
                    'type'=>'say',
                    'from_client_id'=>$client_id,
                    'from_client_name' =>$log['user_phone'],
                    'to_client_id'=>'',
                    'content'=>nl2br(htmlspecialchars($message_data['content'])),
                    'time'=>date('Y-m-d H:i:s'),
                ];
                return Gateway::sendToCurrentClient(json_encode($new_message));
        }
    }

    /**
     * 当客户端断开连接时
     * @param integer $client_id 客户端id
     */
    public static function onClose($client_id)
    {
        // 从房间的客户端列表中删除
        global $db;
        $fd=$db->select('*')->where('user_fd= :id OR party_fd= :party_fd')->bindValues(array('id'=>$client_id,'party_fd'=>$client_id))->from('de_room')->row();
        if($fd['user_fd']==$client_id){
            $db->update('de_room')->cols(array('user_fd'=>NULL))->where('id= '.$fd['id'])->query();
        }else{
            $db->update('de_room')->cols(array('party_fd'=>NULL))->where('id= '.$fd['id'])->query();
        }
        echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']}  client_id:$client_id onClose:''\n";
//        if(isset($_SESSION['room_id']))
//        {
//            $room_id = $_SESSION['room_id'];
//            $new_message = array('type'=>'logout', 'from_client_id'=>$client_id, 'from_client_name'=>'', 'time'=>date('Y-m-d H:i:s'));
//            Gateway::sendToGroup($room_id, json_encode($new_message));
//        }
    }

}

然后进入服务器启动socket服务

到此为止服务端就完成了。

CREATE TABLE `de_room` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `user_id` int(11) NOT NULL COMMENT '房间创建人',
  `user_phone` varchar(255) DEFAULT NULL,
  `party_id` int(11) NOT NULL COMMENT '房间人员iD',
  `accept_phone` varchar(255) DEFAULT NULL,
  `user_fd` varchar(255) DEFAULT NULL COMMENT 'user_id fd',
  `party_fd` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=utf8 COMMENT='房间';
CREATE TABLE `de_chat_log` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL COMMENT '发送方id',
  `user_phone` varchar(255) NOT NULL COMMENT '发送方手机号',
  `accept_id` int(11) NOT NULL COMMENT '接收方ID',
  `accept_phone` varchar(255) NOT NULL COMMENT '发送方手机号',
  `room_id` int(11) NOT NULL COMMENT '房间id',
  `message` text COMMENT '发送内容',
  `create_time` datetime DEFAULT NULL COMMENT '发送时间',
  `is_read` enum('1','2') NOT NULL DEFAULT '1' COMMENT '状态:1=未读,2=已读',
  `is_img` enum('1','2') NOT NULL DEFAULT '1' COMMENT '是否是图片:1=文本,2=图片',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=116 DEFAULT CHARSET=utf8 COMMENT='聊天记录';

 

  • 3
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
在 ThinkPHP 6 中使用 Workerman 实现定时器可以通过添加自定义命令来实现。下面是一个简单的实现步骤: 1. 首先,确保你已经安装了 Workerman 和 think-worker 扩展。可以通过执行以下命令来安装它们: ``` composer require workerman/workerman think-worker ``` 2. 创建一个自定义的命令类来处理定时任务。在 app/command 目录下创建一个名为 Timer.php 的文件,并在该文件中编写以下代码: ```php <?php namespace app\command; use think\console\Command; use think\console\Input; use think\console\Output; class Timer extends Command { protected function configure() { $this->setName('timer:work')->setDescription('Workerman Timer'); } protected function execute(Input $input, Output $output) { $worker = new \Workerman\Worker(); $worker->onWorkerStart = function($worker) { // 在这里编写定时任务的处理逻辑 \Workerman\Lib\Timer::add(1, function() { echo "定时任务执行\n"; }); }; \Workerman\Worker::runAll(); } } ``` 3. 注册自定义命令。在 config/console.php 中的 commands 数组中添加命令类的命名空间路径: ```php 'commands' => [ 'app\command\Timer', ], ``` 4. 运行定时任务。通过执行以下命令来运行定时任务: ``` php think timer:work ``` 这样,定时任务就会在后台运行,并每秒钟执行一次。你可以在 `$worker->onWorkerStart` 回调函数中编写具体的定时任务逻辑。请根据自己的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦夏夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值