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效果,让下拉框在未选择未输 ...
随机推荐
- Shell细小问题汇总
Shell细小问题汇总 本文原文出处: http://blog.csdn.net/bluishglc/article/details/44276607 严禁不论什么形式的转载,否则将托付CSDN官方维 ...
- Cocos2d-x教程(34)-三维物体OBB碰撞检測算法
欢迎增加Cocos2d-x 交流群:193411763 个中心点.1个旋转矩阵和3个1/2边长(注:一个旋转矩阵包括了三个旋转轴,若是二维的OBB包围盒则是一个中心点,两个旋转轴,两个1/2边长). ...
- Java Map 怎样实现Key 的唯一性?
大家都知道.在Map和Set不可存在反复元素? 可是对于内部的细节我们并不了解.今天我们就一块来 探讨一下! 1 对于 HashMap HashSet 他们的底层数据结构的实现是:维护了一张 Ha ...
- Android怎样保证一个线程最多仅仅能有一个Looper?
1. 怎样创建Looper? Looper的构造方法为private,所以不能直接使用其构造方法创建. private Looper(boolean quitAllowed) { mQueue = n ...
- DCS实践干货:使用Redis实现分布式锁
场景介绍 很多互联网场景(如商品秒杀,论坛回帖盖楼等),需要用加锁的方式,以对某种资源进行顺序访问控制.如果应用服务集群部署,则涉及到对分布式应用加锁.当前分布式加锁主要有三种方式:(磁盘)数据库.缓 ...
- Bing Maps进阶系列八:在Bing Maps中集成OpenStreetMap地图
Bing Maps进阶系列八:在Bing Maps中集成OpenStreetMap地图 OSM(OpenStreetMap-开放街道地图)服务就是一种发布自己地图数据图片为服务的一种实现类型,开放街道 ...
- WebView播放H5课件时,锁屏解锁后,页面重新绘制的问题
难题描述:H5页面播放 ,锁屏,解锁后,重新加载了页面,三星不会出现(onpause onstop ,onresume),但在小米.魅族会调用 onpause onstop ondestroy,onr ...
- 洛谷 P1498 南蛮图腾 —— 模拟
题目:https://www.luogu.org/problemnew/show/P1498 大约一年前该做的题...现在来填一下坑: 然而不怎么会模拟!还滚去看TJ了: 就是翻倍复制: \ 这个符号 ...
- IOException 简单解决方法
java.lang.IllegalStateException异常解决方法 这个异常大多数是由文件读取,下载时抛出,但是偶尔也会由类型转换时异常抛出此异常. 错误:Optional int param ...
- Factstone Benchmark(数学)
http://poj.org/problem?id=2661 题意:Amtel在1960年发行了4位计算机,并实行每十年位数翻一番的策略,将最大整数n作为改变的等级,其中n!表示计算机的无符号整数(n ...