websocket学习和群聊实现

数字沐星人
• 阅读 2536
WebSocket协议可以实现前后端全双工通信,从而取代浪费资源的长轮询。在此协议的基础上,可以实现前后端数据、多端数据,真正的实时响应。在学习WebSocket的过程中,实现了一个简化版群聊,过程和代码详细记录在这篇文章中。

本篇文章来自董沅鑫的个人网站,引用、转载请指明出处

查看更多知识,或者技术交流:请访问godbmw.com

1 概述

1.1 WebSocket 是什么?

  1. 建立在 TCP 协议之上的网络通信协议
  2. 全双工通信协议
  3. 没有同源限制
  4. 可以发送文本、二进制数据等

1.2 为什么需要 WebSocket?

了解计算机网络协议的人,应该都知道:HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。

因此,如果在客户端想实时监听服务器变化,必须使用 ajax 来进行轮询,效率低,浪费资源。

而 websocket 就可以使得前后端进行全双工通信(两方都可以向对方进行数据推送),是真正的平等对话

2 WebSocket 客户端

支持HTML5的浏览器支持 WebSocket 协议:

var ws = new WebSocket(url); // 创建一个websocket对象

2.1 WebSocket 属性

属性 描述
ws.readyState 只读属性 readyState 表示连接状态,可以是以下值:0 - 表示连接尚未建立。1 - 表示连接已建立,可以进行通信。2 - 表示连接正在进行关闭。3 - 表示连接已经关闭或者连接不能打开。
ws.bufferedAmount 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

2.2 WebSocket 方法

属性 描述
ws.send() 数据发送
ws.close() 关闭连接

2.3 Websocket 事件

属性 描述
open 连接建立触发
message 通信时触发
error 出错触发
close 关闭连接触发

2.4 代码实现

假设我们在本地8080端口打开了websocket服务,那么,下面代码可以在浏览器中实现和这个服务的通信:

<body>
  <script>
    var ws = new WebSocket("ws://localhost:8080/");
    // 建立连接触发
    ws.onopen = function () {
      ws.send("open ws");
      console.log("open ws");
    };

    // 接收服务端数据触发
    ws.onmessage = function (evt) {
      var data = evt.data;
      console.log("Data is ", data);
    };

    // 断开连接触发
    ws.onclose = function () {
      console.log("close ws");
    };
  </script>
</body>

3 WebSocket 服务端

关于服务端实现,根据技术选型不同,可以选用不同的库和包。我这里使用的是nodews库来websocket服务端。

阮一峰的博文提到的socket.io库,在浏览器端的写法不兼容原生API,准确来说,它们自己实现了一套websocket。所以,使用的时候前后端都应该引用第三方库。这样就造成了代码迁移性,严重下降。

综上所述,ws库有以下优点:

  1. 兼容性好,兼容浏览器原生API
  2. 长期维护,效果稳定
  3. 使用方便(往下看就知道了)

4 实现群聊

4.1 群聊 服务端实现

首先,在命令行中,安装ws库: npm install ws --save

现在,利用ws来实现一个监听8080端口的websocket服务器,讲解都在代码注释里,一目了然

const PORT = 8080; // 监听端口
const WebSocket = require("ws"); // 引入 ws 库

const wss = new WebSocket.Server({ port: PORT }); // 声明wss对象

/**
 * 向除了本身之外所有客户端发送消息,实现群聊功能
 * @param {*} data 要发送的数据
 * @param {*} ws 客户端连接对象
 */
wss.broadcastToElse = function broadcast(data, ws) { 
  wss.clients.forEach(function each(client) {
    if (client !== ws && client.readyState === WebSocket.OPEN) {
      client.send(data);
    }
  });
};

/* 客户端接入,触发 connection */
wss.on("connection", function connection(ws, req) {
  let ip = req.connection.remoteAddress; // 通过req对象可以获得客户端信息,比如:ip,headers等

  /* 客户端发送消息,触发 message */
  ws.on("message", function incoming(message) { 
    ws.send(message); // 向客户端发送消息
    wss.broadcastToElse(message, ws); // 向 其他的 客户端发送消息,实现群聊效果
  });

});

4.2 群聊 客户端实现

为了方便编写,这里引入了jquerybootstrap这两个库,只需要关注js代码即可。

<!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>
  <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
  <script src="https://cdn.bootcss.com/jquery/3.3.0/jquery.min.js"></script>
</head>
<body>
  <div class="container">
    <textarea class="form-control" rows="30" disabled="disabled" id="show-area"></textarea>
    <input type="text" class="form-control" placeholder="请输入聊天内容" id="chat-input">
    <button type="button" class="btn btn-info" id="send-btn">发送</button>
  </div>
  <script>
    var userName = parseInt(Math.random() * 1000, 10) // 随机用户名, 以标识身份
    
    var sendBtn = $("#send-btn"), // 发送信息按钮
      chatInput = $("#chat-input"), // 聊天信息输入框
      showArea = $("#show-area") // 聊天信息展示框

    var ws = new WebSocket("ws://localhost:8080/") // 初始化WebSocket对象

    sendBtn.on("click", function () {
      var content = chatInput.val()
      if (content.length === 0) {
        return alert("请不要输入空白内容")
      }
      content = "At " + (new Date()).toString() + "\n" + "来自用户" + userName + "\n" + content // 拼接用户信息、时间信息和消息
      ws.send(content) // 发送消息
      chatInput.val("") // 清空输入框
    })

    ws.onopen = function () { console.log("Conncet open") }
    ws.onmessage = function (evt) {
      var data = evt.data
      showArea.val(showArea.val() + data + "\n\n") // 刷新聊天信息展示框:显示群聊信息
    }
    ws.onclose = function () { console.log("Connect close") }
  </script>
</body>
</html>

4.3 群聊 效果展示

首先启动我们的服务端代码:node server.js 。其中,server.js是放置服务端代码的文件。

然后,我们打开2次编写的html代码,这相当于,打开2个客户端。来检测群聊功能。

websocket学习和群聊实现

5. 相关资料

本篇文章来自董沅鑫的个人网站,引用、转载请指明出处

查看更多知识,或者技术交流:请访问godbmw.com

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
待兔 待兔
11个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Nginx + lua +[memcached,redis]
精品案例1、Nginxluamemcached,redis实现网站灰度发布2、分库分表/基于Leaf组件实现的全球唯一ID(非UUID)3、Redis独立数据监控,实现订单超时操作/MQ死信操作SelectPollEpollReactor模型4、分布式任务调试Quartz应用
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
ES6 新增的数组的方法
给定一个数组letlist\//wu:武力zhi:智力{id:1,name:'张飞',wu:97,zhi:10},{id:2,name:'诸葛亮',wu:55,zhi:99},{id:3,name:'赵云',wu:97,zhi:66},{id:4,na
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(