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,本文只是将流程写下来,项目使用还是需要将代码优化

tp5 + workerman 实现聊天室_第1张图片

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

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

tp5 + workerman 实现聊天室_第2张图片

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');
    }
}

tp5 + workerman 实现聊天室_第3张图片

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

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

tp5 + workerman 实现聊天室_第4张图片

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服务

tp5 + workerman 实现聊天室_第5张图片

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

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='聊天记录';

 

你可能感兴趣的:(php)