WebSocket实现简易聊天室

前言

详解 HTTP系列之一讲到HTTP/2.0 突破了传统的“请求-问答模式”这一局限,实现了服务器主动向客户端传送数据。而本章将通过一种在单个TCP连接上进行全双工通信的协议–websocket协议,实现一个简单的聊天室(本文转载自xing.org1^大佬的博客,详情请点击此处跳转
 
 

核心要点

聊天室的核心要点是由服务器向每个正在连接的用户发送消息,也就是上面讲到的改变。如果不是这样,只能是客户端以一个很短的时间间隔向服务端请求数据。

要做到广播,就需要server.connections,这个数组记录了所有连接到websocket服务器的用户(也就是进入聊天室的人),通过遍历这个数组,然后给数组中每个连接进来的用户对象发送消息即可。

实现这一功能的代码如下:

const ws = require('nodejs-websocket');
const server = ws.createServer((connect)=>{
	/*
		......
	*/
	
	// 对连接到服务端的每个对象发送数据
	function broadcast(jsonStr){
		server.connections.forEach((element)=>{
		
		//切记,send参数必须是字符串类型,所以这里将对象字符串化
    	element.send(JSON.stringify(jsonStr));
  });
	}
})

 
 

聊天室实现代码

环境:

  • nodejs
  • npm install nodejs-websocket

 
客户端 test.html:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>小石头的群聊</title>
</head>

<body>
  <div class="container">
    <input type="text" placeholder="请输入要发送的内容" id="ipt">
    <button id="btn">发送</button>
    <button id="closeBtn">退出群聊</button>
    <p>
      群聊内容如下:
    </p>
    <div id="rst" style="width: 800px;height: 500px;background-color: greenyellow;"></div>
  </div>
  <script>
    // 聊天室功能
    const ws = new WebSocket('ws://localhost:8081'),
      ipt = document.getElementById('ipt'),
      btn = document.getElementById('btn'),
      closeBtn = document.getElementById('closeBtn'),
      content = document.getElementById('rst');

    // 初次进入聊天室,给个提醒
    ws.addEventListener('open', (e) => {
      console.log('加入聊天室成功', e);
      content.appendChild(creatEle('您已成功加入小石头的群聊~'));		// 
      // 聊天区滚动到底
      goBottom();
    });

    // 接收ws服务器发送的消息,并展示到div#rst当中
    ws.addEventListener('message', (e) => {
      console.log(e.data);
      content.appendChild(creatEle(e.data));
      goBottom();
    });
    
    // 一个带滚动条的DIV元素,怎么让它的滚动条位置默认保持在最底部?
    function goBottom() {
      content.scrollTop = content.scrollHeight;
    }

    // btn被点击时发送请求
    btn.onclick = function () {
      btnClickEvent();
    }
    ipt.addEventListener('keydown', (e) => {
      if (e.keyCode === 13) {
        btnClickEvent();
      }
    });

    // close-btn被点击时退出群聊
    closeBtn.onclick = function () {
      ws.close();
    }

    // 创建一个p标签,存储对应内容,以追加到内容展示区域
    function creatEle(str) {
      console.log(str.indexOf('{'));
      const TYPE_LEAVE = 0; //leave,离开
      const TYPE_ENTRY = 1; //entry,进入
      const TYPE_SPEAK = 2; //speak,发言
      const eleP = document.createElement('p');

      if (str.indexOf('{') == 0) {
        let parseStr = JSON.parse(str);
        eleP.innerHTML = `<span class="timer">${parseStr.time}</span><br/><span class="msg">${parseStr.msg}</span>`;
        switch (parseStr.type) {
          case TYPE_LEAVE:
            eleP.className = 'leave';
            break;
          case TYPE_ENTRY:
            eleP.className = 'entry';
            break;
          case TYPE_SPEAK:
            eleP.className = 'speek';
            break;
          default:
            eleP.className = 'default';
            break;
        }
      } else {
        eleP.innerText = str;
      }
      return eleP;
    }
    // 添加回车发送消息事件
    function btnClickEvent() {
      if (ipt.value.length <= 0) {
        alert('不能发送空消息');
        return;
      }
      ws.send(ipt.value);
      ipt.value = '';
    }
  </script>
</body>
</html>

 
服务端 server.js:

/*
 * @Author: @Guojufeng 
 * @Date: 2019-06-02 19:42:06 
 * @Last Modified by: @Guojufeng
 * @Last Modified time: 2019-06-02 21:39:56
 * 优化 - 加入消息类型和当前时间的响应
 */
const ws = require('nodejs-websocket');
const POST = 8081;

let count = 0;//记录加入人数

const TYPE_LEAVE  = 0;//leave,离开
const TYPE_ENTRY  = 1;//entry,进入
const TYPE_SPEAK  = 2;//speak,发言

const server = ws.createServer((connect)=>{
  count++;// 有人加入,计数加一
  connect.userName = `用户${count}`;//connet是一个对象,新增一个属性用以标记该用户的名字(标识)

  // 1、通知所有人,connect用户加入群聊
  broadcast({
    type: TYPE_ENTRY,
    msg: `${connect.userName}加入群聊。`
  });

  // 2、通知所有人,connect用户发言
  connect.on('text',(rst)=>{
    broadcast({
      type: TYPE_SPEAK,
      msg: `${connect.userName}: ${rst}`
    });
  });

  // 3、通知所有人,connect用户退出群聊
  connect.on('close',(code,reason)=>{//个人认为利用code和reason这里,还可以模拟微信群聊中,用户被群主踢出群的情况
    broadcast({
      type: TYPE_LEAVE,
      msg: `${connect.userName}退出了群聊。`
    });
    count--;// 有人退出,计数减一
  });

  // error事件
  connect.on('error',()=>{
    console.log('发生异常');
  })
});

// 广播功能代码
function broadcast(cont){
  // 利用connections是存放所有加入群聊用户的数组,来给所有人广播内容
  let sendCont = {
    type: cont.type,
    msg: cont.msg,
    time: new Date().toLocaleTimeString()
  };
  server.connections.forEach((element)=>{
    element.send(JSON.stringify(sendCont));//切记,send参数必须是字符串类型,所以这里将对象字符串化
  });
}


server.listen(POST,()=>{
  console.log(`${POST}服务器启动成功。`)
});

 
启动服务:

  1. nodejs执行server.js文件,并成功监听到对应端口
  2. 浏览器打开index.html(可开启多个页面模拟多个用户),进行测试
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值