导语:前几天做了一个繁难的聊天室,实现了聊天性能,聊天内容能够发送文本,图片,音频,视频,表情包等内容,反对一对一聊天,群组聊天。当初就之前的聊天室性能做一个简略的梳理和总结。

目录

  • 原理简述
  • 性能开发
  • 成果体验

原理简述

这次应用了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的常识总结。