导语:前几天做了一个繁难的聊天室,实现了聊天性能,聊天内容能够发送文本,图片,音频,视频,表情包等内容,反对一对一聊天,群组聊天。当初就之前的聊天室性能做一个简略的梳理和总结。
目录
- 原理简述
- 性能开发
- 成果体验
原理简述
这次应用了socket.io
这个工具包进行通信。
webscoket
html5中有websocket的性能,参考这篇文章《html常识总结之WebSocket》理解更多基础知识。
WebSocket是一种在单个 TCP 连贯上进行全双工通信的协定, 能更好的节俭服务器资源和带宽,并且可能更实时地进行通信。
课外科普:
通信分类分为并行通信,串行通信,同步/异步,单工/双工,半双工/全双工。
- 并行通信指的是数据的各位同时在多根数据线上发送或接管。管制简略,传输速度快;因为传输线较多,实用于短距离通信。
串行通信是指数据的各位在同一根数据线上逐位发送和接管。管制简单,传输速度慢;只须要一根数据线,实用于远距离通信。
- 依据对数据流的分界、定时以及同步计划办法不同,可分为和同步串行通信形式和异步通信形式。
依据串行数据的传输方向,咱们能够将通信分为单工,半双工,双工。
- 单工:是指数据传输仅能沿一个方向,不能实现反向传输。
- 半双工:是指数据传输能够沿两个方向,但须要分时进行传输。
- 全双工:是指数据能够同时进行双向传输。
socket.io
socket.io是基于websocket协定的一套成熟的解决方案。长处是性能好,反对多平台;毛病是传输的数据并不齐全遵循websocket协定, 这就要求客户端和服务端都必须应用socket.io的解决方案。
区别
- http和webscoket都是基于tcp;
- http建设的是短连贯;
- websocket建设的是长连贯;
性能开发
当初就这个性能进行剖析并且开发前端和后端内容,先来买通后端局部,为前端连贯socket服务作筹备。
上面这个展现的是最根底的聊天室,包含以下几个性能:
- 多人聊天
- 显示用户名和人数
- 回车发送
- 到顶部
后端方面
这里就是如何在node中建设socket服务器。
装置依赖包
npm install -g express-generatorexpress --view=ejs chatcd chatnpm installnpm install socket.io
配置socket.io
关上bin/www
文件,在var server = http.createServer(app);
上面一行写入以下内容。
上面内容就离开介绍各个内容,不做全副代码粘贴。
- 引入ws服务
const ws = require('socket.io');const io = ws(server, { path: '/chat', transports: [ 'polling', 'websocket' ]})
罕用办法
- 连贯和断开连接
io.on('connection', socket => { console.log('a user connected!'); //disconnect socket.on('disconnect', () => { console.log('a user disconnected!'); })}
- 退出和来到房间
// join roomsocket.join(roomId);// leave roomsocket.leave(roomId);
- 承受音讯
socket.on('event name', data => { // data}
- 发送音讯
socket.emit('event name', { // some data});
- 向其他人播送
socket.broadcast.emit('event name', { // some data});
- 向某个房间发送音讯
io.to(roomId).emit('event name', { // some data})
简易程序
let roomInfo = {};io.on('connection', socket => { let roomId = socket.handshake.query.roomId; // user login socket.on('login', data => { socket.join(roomId); if (!(roomId in roomInfo)) { roomInfo[roomId] = []; } let names = []; let users = roomInfo[roomId]; if (users.length) { for (let i = 0; i < users.length; i++) { names.push(users[i].name); } if (!(names.includes(data.user))) { users.push({ id: socket.id, name: data.name, avatar: data.avatar }) } } else { roomInfo[roomId].push({ id: socket.id, name: data.name, avatar: data.avatar }); } console.log('roomInfo: ', roomInfo); io.to(roomId).emit('system', { name: data.name, users: roomInfo[roomId] }) }) // client msg socket.on('message', data => { io.to(roomId).emit('chat', data); }) // leave room socket.on('leave', data => { let users = roomInfo[roomId]; if (users && users.length) { for (let i = 0; i < users.length; i++) { const user = users[i]; if (data.name == user.name) { users.splice(i, 1); } } } socket.leave(roomId); io.to(roomId).emit('logout', { name: data.name, users: roomInfo[roomId] }) console.log('roomInfo: ', roomInfo); }) socket.on('disconnect', () => { console.log('a user disconnect!'); })});
前端方面
- 引入
socket.io.js
文件
<script src="./js/socket.io.js"></script>
- html局部
登录界面:
<div class="chat"> <h2>XQ聊天室</h2> <form class="chat-form"> <p> <label for="name">昵称:</label> <input type="text" id="name" name="name" placeholder="请输出用户名" required> </p> <p> <label for="avatar">头像:</label> <select name="avatar" id="avatar" required> <option value="avatar1">头像1</option> <option value="avatar2">头像2</option> <option value="avatar3">头像3</option> </select> </p> <p> <label for="roomId">房间:</label> <select name="roomId" id="roomId" required> <option value="1">房间1</option> <option value="2">房间2</option> <option value="3">房间3</option> </select> </p> <p> <input type="submit" value="进入房间"> </p> </form></div>
房间界面:
<div class="room"> <div class="room-header"> <h3>XQ聊天室(<span class="count">0</span>)</h3> <button class="logout">退出</button> </div> <div class="room-nav"> <small>在线人数:</small> <span id="room-users">暂无成员</span> </div> <ul class="room-content"> </ul> <div class="room-footer"> <input class="room-ipt" type="text" placeholder="轻易写点儿吧"> <input class="room-btn" type="submit" value="发送"> </div></div>
- css局部
body { margin: 0; padding: 0; background: #f9f9f9;}h1,h2,h3,h4,h5,h6,p { margin: 0;}ul,li { margin: 0; padding: 0; list-style: none;}.chat { box-sizing: border-box; margin: 50px auto; padding: 20px; width: 300px; height: auto; background: #fff;}.chat.active { display: none;}.chat h2 { margin-bottom: 10px; font-size: 18px; line-height: 1.5; text-align: center;}.chat-form p { display: flex; justify-content: space-between; align-items: center; padding: 5px 0; font-size: 15px; line-height: 35px;}.chat-form p label { width: 50px;}.chat-form p input,.chat-form p select { flex: 1; box-sizing: border-box; padding: 0 10px; height: 30px; border: 1px solid #ccc; outline: none; background: none;}.chat-form p input:focus,.chat-form p select:focus { box-shadow: 0 0 5px #ccc;}.room { display: none; width: 100%; height: 100vh; overflow: hidden;}.room.active { display: flex; flex-direction: column;}.room-header { position: relative; display: flex; justify-content: space-between; align-items: center; box-sizing: border-box; padding: 0 15px; height: 60px; background: #111; color: #fff;}.room-header h3 { font-size: 18px; text-align: left;}.room-header button { width: 50px; height: 50px; background: none; color: #fff; outline: none; border: none; text-align: right;}.room-nav { box-sizing: border-box; padding: 20px 15px; line-height: 30px; font-size: 14px;}.room-nav small,.room-nav span { font-size: 14px;}.room-nav span { color: rgb(6, 90, 146);}.room-content { flex: 1; padding: 15px 0; background: #fff; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; background: #eee; overflow-x: hidden; overflow-y: auto;}.room-content li { display: flex; justify-content: flex-start; align-items: flex-start; box-sizing: border-box; padding: 15px 10px; margin-bottom: 10px; width: 100%;}.room-content li .room-user { display: flex; flex-direction: column; justify-content: center; align-items: center;}.room-content li img { display: inline-block; width: 40px; height: 40px;}.room-content li span { font-size: 14px;}.room-des { position: relative; margin-top: 5px; margin-left: 10px; box-sizing: border-box; padding: 3px 5px; font-size: 14px; line-height: 30px; background: #ccc; border-radius: 5px;}.room-des::before,.room-des::after { content: ''; position: absolute; top: 10px; width: 0; height: 0; border: 5px solid transparent;}.room-des::before { display: inline-block; left: -10px; border-right: 5px solid #ccc;}.room-des::after { display: none; right: -10px; border-left: 5px solid #fff;}.room-me { flex-direction: row-reverse;}.room-me .room-des { margin-left: 0; margin-right: 10px; background: #fff;}.room-me .room-des::before { display: none;}.room-me .room-des::after { display: inline-block;}.room-content .system { justify-content: center; align-items: center; padding: 0; height: 35px; line-height: 35px;}.system p { box-sizing: border-box; padding: 0 5px; font-size: 14px; text-align: center; border-radius: 5px; background: #ccc;}.room-footer { display: flex; justify-content: space-between; align-items: center; height: 50px; background: #fff; border-top: 1px solid #ccc;}.room-footer .room-ipt { margin-top: 1px; box-sizing: border-box; padding: 10px; width: 80%; height: 48px; background: none; border: 1px solid transparent; outline: none;}.room-footer .room-ipt:focus { border: 1px solid #ccc; box-shadow: 0 0 5px #ccc;}.room-footer .room-btn { width: 19%; height: 100%; background: rgb(2, 54, 112); border: 1px solid #ccc; outline: none; font-size: 15px; color: #fff;}
- js局部
登录界面的js
let chat = document.querySelector('.chat');let chatForm = document.querySelector('.chat-form');let user = document.querySelector('#name');let avatar = document.querySelector('#avatar');let roomId = document.querySelector('#roomId');// iolet socket = io.connect('/', { path: '/chat'});// loginchatForm.onsubmit = function(){ let userInfo = { name: user.value, avatar: `/img/${avatar.value}.webp`, roomId: roomId.value } localStorage.setItem('userInfo', JSON.stringify(userInfo)); checkLogin(); return false;};checkLogin();function checkLogin () { let userInfo = localStorage.getItem('userInfo'); userInfo = JSON.parse(userInfo); if (userInfo && userInfo.name) { chat.classList.add('active'); room.classList.add('active'); socket.emit('login', userInfo); } else { chat.classList.remove('active'); room.classList.remove('active'); }}
房间局部的js
let room = document.querySelector('.room');let logout = document.querySelector('.logout');let count = document.querySelector('.count');let roomUsers = document.querySelector('#room-users');let roomContent = document.querySelector('.room-content');let roomIpt = document.querySelector('.room-ipt');let roomBtn = document.querySelector('.room-btn');// 退出登录logout.addEventListener('click', function(){ let userInfo = localStorage.getItem('userInfo'); userInfo = JSON.parse(userInfo); socket.emit('leave', userInfo); alert('退出胜利!'); localStorage.removeItem('userInfo'); checkLogin();})roomIpt.addEventListener('keyup', sendMsg, false);roomBtn.addEventListener('click', sendMsg, false);// 发送音讯 function sendMsg (e) { if (e.type === 'click' || e.code === 'Enter') { let val = roomIpt.value; if (val == '') { alert('聊天内容不能为空!'); return false; } let userInfo = localStorage.getItem('userInfo'); userInfo = JSON.parse(userInfo); userInfo.msg = val; roomIpt.value = ''; socket.emit('message', userInfo); goBot(); }}goBot();// 到底部function goBot () { roomContent.scrollTop = roomContent.scrollHeight;}// 零碎音讯提醒function welcome (user = 'mark', type = 1) { roomContent.innerHTML += ` <li class="system"> <p>零碎音讯:<strong>${user}</strong>${type == 1 ? '来到' : '来到'}本房间!</p> </li> `; goBot();}// 零碎音讯socket.on('system', data => { let strs = ''; welcome(data.name); count.innerText = data.users.length; for (const item of data.users) { strs += item.name + ','; } roomUsers.innerText = ''; roomUsers.innerText += strs;})// 退出揭示socket.on('logout', data => { let strs = ''; welcome(data.name, 2); count.innerText = data.users.length; for (const item of data.users) { strs += item.name + ','; } roomUsers.innerText = ''; roomUsers.innerText += strs;})// 承受音讯socket.on('chat', data => { let userInfo = localStorage.getItem('userInfo'); userInfo = JSON.parse(userInfo); let isUser = data.name == userInfo.name; roomContent.innerHTML += ` <li ${isUser ? 'class="room-me"' : ''}> <div class="room-user"> <img class="room-avatar" src="/chatroom/${(isUser ? userInfo.avatar : data.avatar ) || '/img/avatar1.webp'}" alt=""> <span class="room-name">${isUser ? userInfo.name : data.name }</span> </div> <p class="room-des">${data.msg}</p> </li> `; goBot();})
成果体验
终于做好了,接下来来体验一下网上冲浪--XQ聊天室的美好生活吧!
- 进入房间
首先,输出你本人的昵称,抉择好头像和房间,点击进入房间按钮对话。
- 发送音讯
而后,输出音讯内容,点击发送按钮,或者按Enter
回车也能够。
能够关上一个隐衷无痕窗口或者新的游览器,关上网址,输出另一个测试账号进行
这是jerry登录后的界面
这是mark看到的jerry发来的音讯
- 退出登录
如果聊天完结,能够点击右上方退出聊天室。
这是jerry退出登录后, mark看到的界面
再来看一下后端打印出的用户信息。
- 这是mark登录当前记录的信息
- 这是jerry登录当前记录的信息
- 这是jerry退出当前记录的信息
以上就是一个繁难的聊天室和node中websocket
的常识总结。