博客列表 >IM即时通讯(私聊功能的实现)2019年3月14日

IM即时通讯(私聊功能的实现)2019年3月14日

小明的博客
小明的博客原创
2019年10月07日 09:13:091724浏览

今天,主要实现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;

  • 在调试中要耐心,掌握了整个流程看到出错结果,往回倒退,抽丝剥茧,满满滴就能找到问题所在。


声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议