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
,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();
setTimeout(function(){layim.getMessage(data.data)}, );
break;
// 用户退出 更新用户列表
case 'logout':
break;
//聊天还有不在线
case 'ctUserOutline':
console.log('');
//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 \GatewayWorker\Lib\Gateway;
use \GatewayWorker\Lib\Db;
/**
* 主逻辑
* 主要是处理 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 &
四、案例
GatewayWorker + LayIM实现即时聊天的更多相关文章
- 使用GatewayWorker 开发个即时聊天demo
前言: 上手册以示尊重:https://www.kancloud.cn/walkor/gateway-worker/326138: https://www.cnblogs.com/fuqiang88/ ...
- SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)
SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=>提升,5个Demo贯彻全篇,感兴趣的玩才是真的学) 官方demo:http://www.asp.net/si ...
- Node.js + Web Socket 打造即时聊天程序嗨聊
前端一直是一块充满惊喜的土地,不仅是那些富有创造性的页面,还有那些惊赞的效果及不断推出的新技术.像node.js这样的后端开拓者直接将前端人员的能力扩大到了后端.瞬间就有了一统天下的感觉,来往穿梭于前 ...
- 即时聊天IM之三 XMPP协议客户端库的和Android端框架概述
合肥程序员群:49313181. 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q Q:408365330 E-Mail:egojit@qq.com smack ...
- 即时聊天IM之二 openfire 整合现有系统用户
合肥程序员群:49313181. 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q Q:408365330 E-Mail:egojit@qq.com 综述: ...
- 即时聊天 / XMPP
MQTT是第二个即时聊天协议(了解) 5.即时通讯 即时通讯网上有第三方的解决方案,比如环信,融云等.我们是自己搭的xmpp服务器,服务器使用的tigase,之前写过相关的博客,自己去年也做了对应的w ...
- 7.xmpp版即时聊天
即时聊天的解决方案 socket: xmpp:xmpp+openfire+asmack 环信 常见协议 比较安全,tcp上还加了俩层 简单聊一下socket socket:套接字,连接需要ip和端口, ...
- 类似QQ的应用毗邻(Pilin)即时聊天源码
这个应用是从安卓教程网分享过了的,个人觉得这个还是挺不错的,毗邻(Pilin)即时聊天应用源码,承诺的 基于xmpp openfire asmack 的即时聊天应用,继续完善,现在只完成了文字.表 ...
- 高仿QQ即时聊天软件开发系列之三登录窗口用户选择下拉框
上一篇高仿QQ即时聊天软件开发系列之二登录窗口界面写了一个大概的布局和原理 这一篇详细说下拉框的实现原理 先上最终效果图 一开始其实只是想给下拉框加一个placeholder效果,让下拉框在未选择未输 ...
随机推荐
- Neo4j教程 Neo4j视频教程 Neo4j 图数据库视频教程
课程发布地址 地址: 腾讯课堂<Neo4j 图数据库视频教程> https://ke.qq.com/course/327374?tuin=442d3e14 作者 庞国明,<Neo4j ...
- velt-0.1.7开发: KernelConfig的问题
快乐虾 http://blog.csdn.net/lights_joy/(QQ群:Visual EmbedLinux Tools 375515651) 欢迎转载.但请保留作者信息 VELT的全称是Vi ...
- C++运算符重载的妙用
运算符重载(Operator overloading)是C++重要特性之中的一个,本文通过列举标准库中的运算符重载实例,展示运算符重载在C++里的妙用.详细包含重载operator<<,o ...
- BC - Zball in Tina Town (质数 + 找规律)
Zball in Tina Town Accepts: 541 Submissions: 2463 Time Limit: 3000/1500 MS (Java/Others) Memory ...
- 我不常用的 javascript
获取当前时间:new Date (最后的调用括号可加可不加) 获取当前时间戳: 方法1:Date.parse(new Date()) 方法2:(new Date()).valueOf() 方法3 ...
- R环境中的工作空间(workspace)
工作空间(workspace)就是当前R的工作环境,它储存着全部用户定义的对象(向量.矩阵.函数.数据框.列表) . 在一个R会话结束时,你能够将当前工作空间保存到一个镜像中.并在下次启动R时自己主动 ...
- bzoj2131: 免费的馅饼
首先我们很容易看出是一个DP 然后容易看出是数据结构优化DP 但是这个限制条件有点鬼畜: abs(p[i]-p[j])/2<=(t[i]-t[j]) p[i]>p[j] -> t[i ...
- bzoj 2217 Lollipop
题目大意: 有一个长度为n的序列a1,a2,...,an.其中ai要么是1("W"),要么是2("T") 现在有m个询问,每个询问是询问有没有一个连续的子序列, ...
- JAVA不让类实例化的方法
虽然java是面向对象编程,但也要尽可能避免创建不必要的对象,因为创建过多的对象不仅占用系统资源,而且多了很多不必要的创建销毁对象开销. 那么有哪些避免类创建对象的方法吗? 1,定义私有构造函数.这在 ...
- poj3539 Elevator——同余类bfs
题目:http://poj.org/problem?id=3539 题目大意是给定 a, b, c,求 1~h 内有多少个数可以被 a, b, c 通过加减法组成: 这是今天刚讲的神奇的——同余类 b ...