今天,主要实现IM即时通讯的私聊功能。前台:点击弹出对话框,点击+发送信息。后台:信息发送给服务器,服务器处理后将信息发送给指定用户。
一、前台
功能:点击好友,弹出对话框,点击+发送信息。
实现:给用户头像绑定点击事件,触发chating方法,该方法主要为,通过ajax的get方法把点击的用户的uid发送给chat.php并且跳转,然后执行回调函数,回调函数是layer的弹出层插件,这个chat.php是index.php的弹出曾属于一个页面。chat.php中,通过传过来的uid,连接数据库,找到该用户的信息,取出nickname作为title。然后给+绑定点击事件,调用sends方法,该方法是将对话框中的消息内容和传过来的uid值(通过隐藏域保存),传给index.php的private_msg方法处理。private_msg方法是把消息的type to_uid msg组合成data对象,然后转成json传给服务器。
代码:
// 和TA聊天
function chating(uid){
$.get('/chat.php', {uid:uid}, function (res) {
layer.open({
type:1,
title:false,
closeBtn:0,
area:['100%', '100%'],
content:res
});
}, 'text');
}
//私聊发送消息
function private_msg(to_uid,msg){
var data = new Object();
data.type = 'private_msg';
data.to_uid = to_uid;
data.msg = msg;
ws.send(JSON.stringify(data));
}点击 "运行实例" 按钮查看在线实例
chat.php
<?php
require_once __DIR__.'/lib/common.php';
require_once __DIR__.'/lib/Db.php';
//获取get传过来的uid
$uid = (int)get('uid');
//连接数据库
$db = new Db();
$title = '';
//获取点击朋友的信息
$user = $db->table('member')->where(array('uid'=>$uid))->item();
$title = $user['nickname'];
?>
<style type="text/css">
.layui-layer-page{background: #f1f1f1;}
.chat-header{margin-top: 1rem;text-align: center;}
.chats{position: fixed;bottom: 0px;height: 3.5rem;line-height: 3.5rem;background: #f1f1f1;width: 100%;padding: 0.5rem 0rem;border-top: 1px solid #ddd;}
.chats i{font-size: 1.5rem;}
.chats .layui-col-xs1{text-align: center;line-height: 2.8rem;}
.chats .txt-chat{overflow-y: auto;background: #fff;height: 2rem;line-height: 1rem;padding: 5px;margin-right: 5px;}
.msgs{margin-bottom: 3.8rem;}
</style>
<input type="hidden" id="uid" value="<?php echo $uid;?>">
<!--头部菜单-->
<div class="layui-container">
<div class="chat-header">
<i class="layui-icon" style="float: left;" onclick="chat_close()"></i>
<span><?php echo $title;?></span>
<i class="layui-icon" style="float: right;"></i>
</div>
</div>
<hr>
<!--消息区-->
<div class="msg_list" id="msg_list">
</div>
<!--聊天区-->
<div class="chats layui-container">
<div class="layui-col-xs1"><i class="layui-icon"></i></div>
<div class="layui-col-xs9"><div class="txt-chat" contenteditable="true"></div></div>
<div class="layui-col-xs1"><i class="layui-icon" style="font-size: 1.4rem;"></i></div>
<div class="layui-col-xs1"><i class="layui-icon" onclick="sends()"></i></div>
</div>
<script type="text/javascript">
// 关闭chat
function chat_close(){
layer.closeAll();
}
// 发送消息
function sends(){
var to_uid = $('#uid').val();
var msg = $('.txt-chat').html();
private_msg(to_uid, msg);
$('.txt-chat').html('');s
}
</script>点击 "运行实例" 按钮查看在线实例
二、后台
功能:服务器收到消息后,然后转发给目标用户。
实现:在连接刚建立时,服务器记录用户登陆信息建立redis哈希表(连接序号和用户信息对照表)之外,还需要建立用户数据库uid和服务器连接序号对应哈希表,方便服务器分配发送信息。服务器收到数据后,通过Chat类的process_msg方法判断data['type'],如果是私聊类型,那么交给process_private_msg处理,该方法调用发送过来的数据,通过之前建立的用户uid和服务器连接序号对照表,找到目标用户uid的服务器连接序号(ws_uid),然后获取到发送源用户的nickname,avatar,发送事件等数据,通过循环遍历找到连接序号和目标用户的ws_uid相符的连接对象$conn 然后该连接将发送源用户的相关信息,一对一的发送给目标用户客户端,最后在前端收到数据后,调用onmessage事件,渲染出来。
//处理登陆的信息
private function process_login ($data) {
$user_json = $this->aes->decrypt($data);
$user_info = json_decode($user_json, true);
if ($user_info['uid'] <= 0) {
return;
}
$this->redis->hSet($this->hash_wsuid_user_key, $this->connection->uid, $user_json);
$this->redis->hSet('chat_uid_wsuid_list', $user_info['uid'], $this->connection->uid);
}
//处理私聊的信息
private function process_private_msg ($data) {
global $ws_worker;
//1、通过传过来的目标用户的uid找到服务器给连接对象分配的ws_uid
$ws_uid = $this->redis->hGet('chat_uid_wsuid_list', $data['to_uid']);
//拿到发送者用户详细信息
$send_user_json = $this->redis->hGet($this->hash_wsuid_user_key, $this->connection->uid);
$send_user_info = json_decode($send_user_json, true);
//2、通过ws_uid找到目标用户在服务器上的连接对象
$connection_list = $ws_worker->connections;
foreach ($connection_list as $conn) {
if ($conn->uid == $ws_uid) {
$data['nickname'] = $send_user_info['nickname'];
$data['avatar'] = $send_user_info['avatar'];
$data['send_time'] = date('Y-m-d H:i:s');
$conn->send(json_encode($data));
break;
}
}
}
}点击 "运行实例" 按钮查看在线实例
ws.onmessage = function (ev) {
console.log(ev.data);
var obj_msg = $.parseJSON(ev.data);
var html = '<div class="item">\
<img class="avatar" src="'+obj_msg.avatar+'">\
<div class="userinfo">\
<p ondblclick="menu(this)"><span class="username" >'+obj_msg.nickname+'</span><span class="layui-badge-rim times">'+obj_msg.send_time+'</span></p>\
<div class="msg"><div class="layui-badge" style="height: 100%;max-width: 200px;background:#fff;color:#333">'+obj_msg.msg+'</div></div>\
</div>\
</div>';
$('#msg_list').append(html);
}点击 "运行实例" 按钮查看在线实例
三、总结
我写代码遇到的坑
function handle_message ($connection, $data) {
global $chat;
$chat->connection($connection);
$data = json_decode($data, true);
$chat->process_msg($data);
}点击 "运行实例" 按钮查看在线实例
之前的处理数据在给连接对象赋序号之前,所以导致最后在将数据(uid和wsuid数据表)存入redis时,第一个uid下没有ws_uid;
在调试中要耐心,掌握了整个流程看到出错结果,往回倒退,抽丝剥茧,满满滴就能找到问题所在。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号