今天,主要實現(xiàn)IM即時通訊的私聊功能。前臺:點擊彈出對話框,點擊+發(fā)送信息。后臺:信息發(fā)送給服務(wù)器,服務(wù)器處理后將信息發(fā)送給指定用戶。
一、前臺
功能:點擊好友,彈出對話框,點擊+發(fā)送信息。
實現(xiàn):給用戶頭像綁定點擊事件,觸發(fā)chating方法,該方法主要為,通過ajax的get方法把點擊的用戶的uid發(fā)送給chat.php并且跳轉(zhuǎn),然后執(zhí)行回調(diào)函數(shù),回調(diào)函數(shù)是layer的彈出層插件,這個chat.php是index.php的彈出曾屬于一個頁面。chat.php中,通過傳過來的uid,連接數(shù)據(jù)庫,找到該用戶的信息,取出nickname作為title。然后給+綁定點擊事件,調(diào)用sends方法,該方法是將對話框中的消息內(nèi)容和傳過來的uid值(通過隱藏域保存),傳給index.php的private_msg方法處理。private_msg方法是把消息的type to_uid msg組合成data對象,然后轉(zhuǎn)成json傳給服務(wù)器。
代碼:
// 和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'); } //私聊發(fā)送消息 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'); //連接數(shù)據(jù)庫 $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> <!--消息區(qū)--> <div class="msg_list" id="msg_list"> </div> <!--聊天區(qū)--> <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"> // 關(guān)閉chat function chat_close(){ layer.closeAll(); } // 發(fā)送消息 function sends(){ var to_uid = $('#uid').val(); var msg = $('.txt-chat').html(); private_msg(to_uid, msg); $('.txt-chat').html('');s } </script>
點擊 "運行實例" 按鈕查看在線實例
二、后臺
功能:服務(wù)器收到消息后,然后轉(zhuǎn)發(fā)給目標(biāo)用戶。
實現(xiàn):在連接剛建立時,服務(wù)器記錄用戶登陸信息建立redis哈希表(連接序號和用戶信息對照表)之外,還需要建立用戶數(shù)據(jù)庫uid和服務(wù)器連接序號對應(yīng)哈希表,方便服務(wù)器分配發(fā)送信息。服務(wù)器收到數(shù)據(jù)后,通過Chat類的process_msg方法判斷data['type'],如果是私聊類型,那么交給process_private_msg處理,該方法調(diào)用發(fā)送過來的數(shù)據(jù),通過之前建立的用戶uid和服務(wù)器連接序號對照表,找到目標(biāo)用戶uid的服務(wù)器連接序號(ws_uid),然后獲取到發(fā)送源用戶的nickname,avatar,發(fā)送事件等數(shù)據(jù),通過循環(huán)遍歷找到連接序號和目標(biāo)用戶的ws_uid相符的連接對象$conn 然后該連接將發(fā)送源用戶的相關(guān)信息,一對一的發(fā)送給目標(biāo)用戶客戶端,最后在前端收到數(shù)據(jù)后,調(diào)用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、通過傳過來的目標(biāo)用戶的uid找到服務(wù)器給連接對象分配的ws_uid $ws_uid = $this->redis->hGet('chat_uid_wsuid_list', $data['to_uid']); //拿到發(fā)送者用戶詳細(xì)信息 $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找到目標(biāo)用戶在服務(wù)器上的連接對象 $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); }
點擊 "運行實例" 按鈕查看在線實例
三、總結(jié)
我寫代碼遇到的坑
function handle_message ($connection, $data) { global $chat; $chat->connection($connection); $data = json_decode($data, true); $chat->process_msg($data); }
點擊 "運行實例" 按鈕查看在線實例
之前的處理數(shù)據(jù)在給連接對象賦序號之前,所以導(dǎo)致最后在將數(shù)據(jù)(uid和wsuid數(shù)據(jù)表)存入redis時,第一個uid下沒有ws_uid;
在調(diào)試中要耐心,掌握了整個流程看到出錯結(jié)果,往回倒退,抽絲剝繭,滿滿滴就能找到問題所在。
微信掃碼
關(guān)注PHP中文網(wǎng)服務(wù)號
QQ掃碼
加入技術(shù)交流群
Copyright 2014-2025 http://ipnx.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號