今天,主要实现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;
在调试中要耐心,掌握了整个流程看到出错结果,往回倒退,抽丝剥茧,满满滴就能找到问题所在。