• GatewayWorker + LayIM实现即时聊天


    一、程序目录结构

    二、代码展示

    附LayIM开发文档:https://www.layui.com/doc/modules/layim.html

    1、前端代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>websocket连接</title>
        <link rel="stylesheet" href="./layui/css/layui.css" media="all">
        <script src="./layui/jquery.min.js"></script>
        <script src="./layui/layui.js"></script>
    </head>
    <body style="background: url(./layui/images/irongrip.png) repeat">
        
    </body>
    <script>
        layui.use('layim', function(layim){
        //基础配置
        layim.config({
            //获取主面板列表信息
            init: {
              url: "./getUserList.php?id=<?php echo $_GET['id']?>" //接口地址(返回的数据格式见下文)
              ,type: 'get' //默认get,一般可不填
              ,data: {} //额外参数
            }
            //获取群员接口
            ,members: {
              url: "./getMembers.php" //接口地址(返回的数据格式见下文)
              ,type: 'get' //默认get,一般可不填
              ,data: {} //额外参数
            },
            uploadFile: {
                url: ""
            }
            ,uploadImage: {
                url: ""
            }
            ,brief: false //是否简约模式(默认false,如果只用到在线客服,且不想显示主面板,可以设置 true)
            ,title: '我的LayIM' //主面板最小化后显示的名称
            ,maxLength: 3000 //最长发送的字符长度,默认3000
            ,isfriend: true //是否开启好友(默认true,即开启)
            ,isgroup: true //是否开启群组(默认true,即开启)
            ,right: '0px' //默认0px,用于设定主面板右偏移量。该参数可避免遮盖你页面右下角已经的bar。
            ,chatLog: "" //聊天记录地址(如果未填则不显示)
            ,find: "" //查找好友/群的地址(如果未填则不显示)
            ,copyright: false //是否授权,如果通过官网捐赠获得LayIM,此处可填true
        });
    
        var socket = new WebSocket("ws://127.0.0.1:7272");
        socket.onopen = function(){
            <?php 
    
                $id = $_GET['id'];
                $conn = @mysql_connect("127.0.0.1", "root", "root") or die("连接数据库失败");
                mysql_select_db("layim");
                $rows = mysql_query("select * from snake_chatuser where id = $id");
                $info = array();
                while ($row = mysql_fetch_array($rows)) {
                    $info = $row;
                }
            ?>
            var id = '<?php echo $info['id'];?>';
            var username = '<?php echo $info['username'];?>';
            var avatar = '<?php echo $info['avatar'];?>';
            var sign = '<?php echo $info['sign'];?>';
            //登录
            var login_data = '{"type":"init","id":"'+id+'","username":"'+username+'","avatar":"'+avatar+'","sign":"'+sign+'"}';
            socket.send( login_data );
            console.log("websocket握手成功!"); 
        };
    
        //监听收到的消息
        socket.onmessage = function(res){
            // console.log(res);
            var data = eval("("+res.data+")");
            switch(data['message_type']){
                // 服务端ping客户端
                case 'ping':
                    socket.send('{"type":"ping"}');
                    break;
                // 登录 更新用户列表
                case 'init':
                    //console.log(data['id']+"登录成功");
                    //layim.getMessage(res.data); //res.data即你发送消息传递的数据(阅读:监听发送的消息)
                    break;
                //添加 用户
                case 'addUser':
                    //console.log(data.data);
                    layim.addList(data.data);
                    break;
                //删除 用户
                case 'delUser':
                    layim.removeList({
                        type: 'friend'
                        ,id: data.data.id //好友或者群组ID
                    });
                    break;
                // 添加 分组信息
                case 'addGroup':
                   // console.log(data.data);
                    layim.addList(data.data);
                    break;
                case 'delGroup':
                    layim.removeList({
                        type: 'group'
                        ,id: data.data.id //好友或者群组ID
                    });
                    break;
                // 检测聊天数据
                case 'chatMessage':
                    //console.log(data.data);
                    layim.getMessage(data.data);
                    break;
                // 离线消息推送
                case 'logMessage':
                console.log(1);
                    setTimeout(function(){layim.getMessage(data.data)}, 1000);
                    break;
                // 用户退出 更新用户列表
                case 'logout':
                    break;
                //聊天还有不在线
                case 'ctUserOutline':
                    console.log('11111');
                    //layer.msg('好友不在线', {'time' : 1000});
                    break;
                   
            }
        };
    
        //layim建立就绪
        layim.on('ready', function(res){
    
            layim.on('sendMessage', function(res){
                console.log(res);
                // 发送消息
                var mine = JSON.stringify(res.mine);
                var to = JSON.stringify(res.to);
                var login_data = '{"type":"chatMessage","data":{"mine":'+mine+', "to":'+to+'}}';
                socket.send( login_data );
    
            });
        });
    });
        
    </script>
    </html>

    2、后台核心代码

    <?php
    use GatewayWorkerLibGateway;
    use GatewayWorkerLibDb;
    /**
     * 主逻辑
     * 主要是处理 onConnect onMessage onClose 三个方法
     * onConnect 和 onClose 如果不需要可以不用实现并删除
     */
    class Events
    {
      public static function onConnect($client_id) {
        
      }
       /**
        * 当客户端发来消息时触发
        * @param int $client_id 连接id
        * @param mixed $message 具体消息
        */
       public static function onMessage($client_id, $data) {
           $message = json_decode($data, true);
           $message_type = $message['type'];
           switch($message_type) {
               case 'init':
                   // uid
                   $uid = $message['id'];
                   // 设置session
                   $_SESSION = [
                       'username' => $message['username'],
                       'avatar'   => $message['avatar'],
                       'id'       => $uid,
                       'sign'     => $message['sign']
                   ];
    
                   // 将当前链接与uid绑定
                   Gateway::bindUid($client_id, $uid);
                   // 通知当前客户端初始化
                   $init_message = array(
                       'message_type' => 'init',
                       'id'           => $uid,
                   );
                   Gateway::sendToClient($client_id, json_encode($init_message));
    
                   //查询最近1周有无需要推送的离线信息
                   $db1 = Db::instance('db1');  //数据库链接
                   $time = time() - 7 * 3600 * 24;
                   $resMsg = $db1->select('id,fromid,fromname,fromavatar,timeline,content')->from('snake_chatlog')
                       ->where("toid= {$uid} and timeline > {$time} and type = 'friend' and needsend = 1" )
                       ->query();
                    //var_export($resMsg);
                   if( !empty( $resMsg ) ){
    
                       foreach( $resMsg as $key=>$vo ){
    
                           $log_message = [
                               'message_type' => 'logMessage',
                               'data' => [
                                   'username' => $vo['fromname'],
                                   'avatar'   => $vo['fromavatar'],
                                   'id'       => $vo['fromid'],
                                   'type'     => 'friend',
                                   'content'  => htmlspecialchars( $vo['content'] ),
                                   'timestamp'=> $vo['timeline'] * 1000,
                               ]
                           ];
    
                           Gateway::sendToUid( $uid, json_encode($log_message) );
    
                           //设置推送状态为已经推送
                           $db1->query("UPDATE `snake_chatlog` SET `needsend` = '0' WHERE id=" . $vo['id']);
    
                       }
                   }
    
                   //查询当前的用户是在哪个分组中,将当前的链接加入该分组
                   $ret = $db1->query("select `groupid` from `snake_groupdetail` where `userid` = {$uid} group by `groupid`");
                   if( !empty( $ret ) ){
                       foreach( $ret as $key=>$vo ){
                           Gateway::joinGroup($client_id, $vo['groupid']);  //将登录用户加入群组
                       }
                   }
                   unset( $ret );
                   return;
                   break;
               case 'addUser' :
                   //添加用户
                   $add_message = [
                       'message_type' => 'addUser',
                       'data' => [
                           'type' => 'friend',
                           'avatar'   => $message['data']['avatar'],
                           'username' => $message['data']['username'],
                           'groupid'  => $message['data']['groupid'],
                           'id'       => $message['data']['id'],
                           'sign'     => $message['data']['sign']
                       ]
                   ];
                   Gateway::sendToAll( json_encode($add_message), null, $client_id );
                   return;
                   break;
               case 'delUser' :
                   //删除用户
                   $del_message = [
                       'message_type' => 'delUser',
                       'data' => [
                           'type' => 'friend',
                           'id'       => $message['data']['id']
                       ]
                   ];
                   Gateway::sendToAll( json_encode($del_message), null, $client_id );
                   return;
                   break;
               case 'addGroup':
                   //添加群组
                   $uids = explode( ',', $message['data']['uids'] );
                   $client_id_array = [];
                   foreach( $uids as $vo ){
                        $ret = Gateway::getClientIdByUid( $vo );  //当前组中在线的client_id
                        if( !empty( $ret ) ){
                            $client_id_array[] = $ret['0'];
    
                            Gateway::joinGroup($ret['0'], $message['data']['id']);  //将这些用户加入群组
                        }
                   }
                   unset( $ret, $uids );
    
                   $add_message = [
                       'message_type' => 'addGroup',
                       'data' => [
                           'type' => 'group',
                           'avatar'   => $message['data']['avatar'],
                           'id'       => $message['data']['id'],
                           'groupname'     => $message['data']['groupname']
                       ]
                   ];
                   Gateway::sendToAll( json_encode($add_message), $client_id_array, $client_id );
                   return;
                   break;
               case 'joinGroup':
                   //加入群组
                   $uid = $message['data']['uid'];
                   $ret = Gateway::getClientIdByUid( $uid ); //若在线实时推送
                   if( !empty( $ret ) ){
                       Gateway::joinGroup($ret['0'], $message['data']['id']);  //将该用户加入群组
    
                       $add_message = [
                           'message_type' => 'addGroup',
                           'data' => [
                               'type' => 'group',
                               'avatar'   => $message['data']['avatar'],
                               'id'       => $message['data']['id'],
                               'groupname'     => $message['data']['groupname']
                           ]
                       ];
                       Gateway::sendToAll( json_encode($add_message), [$ret['0']], $client_id );  //推送群组信息
                   }
    
                   return;
                   break;
               case 'addMember':
                   //添加群组成员
                   $uids = explode( ',', $message['data']['uid'] );
                   $client_id_array = [];
                   foreach( $uids as $vo ){
                       $ret = Gateway::getClientIdByUid( $vo );  //当前组中在线的client_id
                       if( !empty( $ret ) ){
                           $client_id_array[] = $ret['0'];
    
                           Gateway::joinGroup($ret['0'], $message['data']['id']);  //将这些用户加入群组
                       }
                   }
                   unset( $ret, $uids );
    
                   $add_message = [
                       'message_type' => 'addGroup',
                       'data' => [
                           'type' => 'group',
                           'avatar'   => $message['data']['avatar'],
                           'id'       => $message['data']['id'],
                           'groupname'     => $message['data']['groupname']
                       ]
                   ];
                   Gateway::sendToAll( json_encode($add_message), $client_id_array, $client_id );  //推送群组信息
                   return;
                   break;
               case 'removeMember':
                   //将移除群组的成员的群信息移除,并从讨论组移除
                   $ret = Gateway::getClientIdByUid( $message['data']['uid'] );
                   if( !empty( $ret ) ){
    
                       Gateway::leaveGroup($ret['0'], $message['data']['id']);
    
                       $del_message = [
                           'message_type' => 'delGroup',
                           'data' => [
                               'type' => 'group',
                               'id'       => $message['data']['id']
                           ]
                       ];
                       Gateway::sendToAll( json_encode($del_message), [$ret['0']], $client_id );
                   }
    
                   return;
                   break;
               case 'delGroup':
                   //删除群组
                   $del_message = [
                       'message_type' => 'delGroup',
                       'data' => [
                           'type' => 'group',
                           'id'       => $message['data']['id']
                       ]
                   ];
                   Gateway::sendToAll( json_encode($del_message), null, $client_id );
                   return;
                   break;
               case 'chatMessage':
                   $db1 = Db::instance('db1');  //数据库链接
                   // 聊天消息
                   $type = $message['data']['to']['type'];
                   $to_id = $message['data']['to']['id'];
                   $uid = $message['data']['mine']['id'];
     
                   $chat_message = [
                        'message_type' => 'chatMessage',
                        'data' => [
                            'username' => $message['data']['mine']['username'],
                            'avatar'   => $message['data']['mine']['avatar'],
                            'id'       => $type === 'friend' ? $uid : $to_id,
                            'type'     => $type,
                            'content'  => htmlspecialchars($message['data']['mine']['content']),
                            'timestamp'=> time()*1000,
                        ]
                   ];
                   //聊天记录数组
                   $param = [
                       'fromid' => $uid,
                       'toid' => $to_id,
                       'fromname' => $message['data']['mine']['username'],
                       'fromavatar' => $message['data']['mine']['avatar'],
                       'content' => htmlspecialchars($message['data']['mine']['content']),
                       'timeline' => time(),
                       'needsend' => 0
                   ];
                   switch ($type) {
                       // 私聊
                       case 'friend':
                           // 插入
                           $param['type'] = 'friend';
                           if( empty( Gateway::getClientIdByUid( $to_id ) ) ){
                               $param['needsend'] = 1;  //用户不在线,标记此消息推送
                           }
                           $db1->insert('snake_chatlog')->cols( $param )->query();
                           return Gateway::sendToUid($to_id, json_encode($chat_message));
                       // 群聊
                       case 'group':
                           $param['type'] = 'group';
                           $db1->insert('snake_chatlog')->cols( $param )->query();
                           return Gateway::sendToGroup($to_id, json_encode($chat_message), $client_id);
                   }
                   return;
                   break;
               case 'hide':
               case 'online':
                   $status_message = [
                       'message_type' => $message_type,
                       'id'           => $_SESSION['id'],
                   ];
                   $_SESSION['online'] = $message_type;
                   Gateway::sendToAll(json_encode($status_message));
                   return;
                   break;
               case 'ping':
                   return;
               default:
                   echo "unknown message $data" . PHP_EOL;
           }
       }
       
       /**
        * 当用户断开连接时触发
        * @param int $client_id 连接id
        */
       public static function onClose($client_id) {
           $logout_message = [
               'message_type' => 'logout',
               'id'           => $_SESSION['id']
           ];
           Gateway::sendToAll(json_encode($logout_message));
       }
    }

    三、页面效果图

    有需要了解的加QQ:2575404985,可以兼容linux与windows

    在linux下运行方式,在workerman目录下创建start.sh,代码如下:

    #!/bin/bash
    php ./Applications/start_register.php start &
    php ./Applications/start_gateway.php start &
    php ./Applications/start_businessworker.php start &

     四、案例

  • 相关阅读:
    小知识!
    命令级的python静态资源服务。
    自定义滚动条样式-transition无效
    css:a:visited限制
    react16 渲染流程
    virtual-dom
    用video标签流式加载
    golang 代码笔记
    position:fixed not work?
    go/node/python 多进程与多核cpu
  • 原文地址:https://www.cnblogs.com/yang-2018/p/10435157.html
Copyright © 2020-2023  润新知