上一章,我们学习和了解了websocket 是什么以及初始搭建,接下来,我们继续了解,如何进行广播以及对应的私聊呢。
首先,就以uwebsockets
当前提供的api来看,比较少,主要就是订阅/发布接口,那么我们如果要实现这个广播和私聊的话,就应该创建多个频道,来根据频道来进行区分私聊和不同的广播等。
接下来,我们将要实现这样一个需求,先做一个简易版本的聊天室:
用户输入用户名进行登录 展示聊天室内所有成员 聊天室内成员消息互通
思路如下:
那么,问题来啦..如果用户退出了(或者直接关闭浏览器了怎么办,是不是得需要更新成员信息?这个后续章节再处理,我觉的应该是心跳包可以解决吧)
我们先来做一个简单的页面,大体是长这个样子的。
一个对话框,一个当前用户列表,以及一个消息发送区域。
接下来,我们要实现的功能很简单,用户进入页面,如果是第一次登陆,则需要输入名称,然后随机分配一个ID存储在本地缓存中。 然后用户登陆成功后获得当前在线的用户列表信息并展示,然后用户在底部输入内容点击发送后,当前聊天室内的所有人都可以看到该信息。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>聊天室</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="room">
<div class="title">聊天室----ChatRoom</div>
<div class="container">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="footer">
<textarea name="content" id="content" placeholder="输入内容点击发送或回车"></textarea>
<div class="btn" filter="send">消息发送</div>
</div>
</div>
<script>
//检查当前用户数据
var user = localStorage.getItem('localUser');
if(null == user || user == ''){
var name = prompt('请输入您的姓名.');
//做存储,此处没做强校验,正常应该是从服务端拿到用户信息,确保唯一的。
var uid = +new Date();
//随机一个头像
var avatar = 'avatar/'+(Math.ceil(Math.random()*39))+'.jpg';
user = {name : name,id : uid,avatar : avatar};
var str = JSON.stringify(user);
localStorage.setItem('localUser',str);
}
user = typeof user == 'object' ? user : JSON.parse(user);
//事件绑定
var eventMap = {
sayHi : function(dom){
alert('3')
ws.send(JSON.stringify({type : 'hello',content : '你好'}))
},
formatMsg : function(type,content){
return JSON.stringify({
name : user.name,
id : user.id,
type : type,
avatar : user.avatar,
content : content||''//此处未处理,后续还需要对内容进行处理转义
})
},
//服务端发送消息处理
//上线成功反馈
online : function(data){
var users = data.data;
document.querySelector('.right').innerHTML = users.map(function(item){
return '<div class="user-item" id="'+item.id+'"><div class="right-avatar"><img src="'+item.avatar+'" /></div>'+item.name+'</div>';
}).join('');
},
//广播消息
tip : function(data){
var div = document.createElement('div');
div.innerHTML = data.msg;
div.className ='msg-tip';
document.querySelector('.left').appendChild(div);
},
//当前用户消息发送
send : function(){
var textarea =document.getElementById('content');
var content = textarea.value;
ws.send(eventMap.formatMsg('msg',content));
textarea.value = '';
textarea.focus();
},
//广播消息--群聊
msg : function(data){
//区分自己和别人
var data = data.data;
//对广播消息进行处理。
var container = document.querySelector('.left');
var div = document.createElement('div');
div.className = 'msg-block';
div.innerHTML = '<div class="info"><div class="info-avatar"><img src="'+data.avatar+'" /></div><div class="info-name">'+data.name+': </div></div><div class="msg-content">'+data.content+'</div>';
container.appendChild(div);
//滚动条移动到最后
container.scrollTop = container.scrollHeight;
}
};
document.querySelectorAll('[filter]').forEach((item)=>{
var eventType = item.getAttribute('filter');
item.onclick = eventMap[eventType];
});
//websocket 处理
var ws = new WebSocket("ws://localhost:9001");
ws.onopen = function(evt) {
console.log("Connection open ...");
//链接成功后,发送消息告知服务端用户上线
ws.send(eventMap.formatMsg('online'));
};
ws.onmessage = function(evt) {
var data = evt.data;
//接受到服务端的消息,并对消息进行分类处理
data = JSON.parse(data);
console.log(data);
eventMap[data.event].call(null,data);
};
ws.onclose = function(evt) {
console.log("Connection closed.");
};
</script>
</body>
</html>
html,body,.room{
margin:0px;
padding:0px;
height:100%;
width:100%;
overflow:hidden;
}
.room{
display:flex;
flex-direction: column;
}
.title{
height:50px;
text-align:center;
font-size:20px;
color:#333;
border-bottom:1px solid #999;
display:flex;
align-items: center;
justify-content: center;
}
.footer{
height:100px;
display:flex;
flex-direction: row;
align-items: center;
}
.btn{
background-color: #f60;
color:white;
height:100px;
line-height:100px;
cursor:pointer;
padding:0px 30px;
}
.footer textarea{
flex-grow: 1;
height:100px;
outline:none;
border:1px solid #ccc;
resize: none;
padding:10px;
box-sizing:border-box;
}
.container{
flex-grow:1;
display: flex;
flex-direction: row;
height:100%;
overflow-y:auto;
}
.left{
flex:8;
overflow-y:auto;
}
.right{
flex:2;
border-left:1px solid #aaa;
overflow-y:auto;
}
.user-item{
height:45px;
line-height:45px;
padding-left:50px;
border-bottom:1px dashed #ccc;
display:flex;
flex-direction: row;
font-size:30px;
}
.user-item img{
height:35px;
margin-right:5px;
}
.msg-tip{
text-align:center;
font-size:14px;
color:#aaa;
margin:10px 0px;
}
/**消息展示**/
.msg-block{
display:flex;
flex-direction: row;
margin-bottom:10px;
padding:0px 15px;
}
.msg-block .info{
display:flex;
flex-direction: row;
align-items:center;
font-size:20px;
}
.info-avatar{
display:flex;
flex-direction: row;
align-items: center;
}
.msg-block .info .info-avatar img{
height:20px;
}
.msg-content{
background-color:rgba(0,0,0,0.65);
color:white;
padding:10px;
margin-left:15px;
margin-right:20px;
border-radius:5px;
}
/***
* 针对消息进行处理
*/
let users = [];//当前用户信息
const msgMap = {
//用户上线
online : (ws,data)=>{
console.log(data);
//检查users 是否存在重复
let prevUser = users.find(item=>item.id == data.id);
if(!prevUser){
users.push(data);
}
//广播当前用户上线
ws.publish('broadcast',JSON.stringify({event : 'tip',msg : '用户:['+data.name+']已加入该房间'}));
//回馈消息,并告知当前所有用户数据
ws.send(JSON.stringify({event : 'online',success : true,msg : '上线成功',data : users}));
},
//接收到用户发送的消息
msg : (ws,data)=>{
//收到消息进行广播,如果后续保留历史记录,此处还需要做处理
ws.publish('broadcast',JSON.stringify({event : 'msg',data : data}))
},
default : (ws,data)=>{
ws.send('hello , this is ws server');
},
hello : (ws,data)=>{
ws.publish('broadcast','hi',false);
}
};
module.exports = function(content,ws){
try{
let obj = JSON.parse(content);
msgMap[obj.type].call(null,ws,obj);
}catch(e){
msgMap['default'].call(null,ws,content);
}
}
完成后,新开两个浏览器,加入聊天室,就可以很嗨皮的畅聊了...
但是.. 功能是不是感觉太简单了,如果我想跟某人私聊怎么办?能不能知道某用户是否还在线?是否可以发图片啊、表情啊?
接下来,我们继续实现以下功能:
后续还将会继续实现以下功能,由于博主是做教育行业的,所以准备做个画板功能..
我将逐一实现以上功能,最后会提供每一章节的源代码,供大家下载参考。
转载请注明出处: https://chrunlee.cn/article/websocket-learn-day2.html