WebSocket

68 阅读10分钟

什么是WebSocket?

实时通讯 WebSocket是一种协议,它被设计出来的目的就是在客户端和服务端提供低延迟,全双工和长期运行的连接

下面一幅图是 HTTP 建立连接与 websocket 建立连接的流程图

image.png

全双工

通信的两个参与方可以同时fa送和接收数据,不需要等待对方的响应或传输完成,通过建立全双工的持久连接,客户端和服务器之间,就能实现高效实时性更强的通信

传统通信: 电子邮件,网页浏览,存在延迟,需要用户主动请求来获取更新数据 实时通讯: 即时消息传递,音视频通话,在线会议和实时数据传输等,可以实现即时的数据传输和交流,不需要用户主动过请求或舒心来获取更新数据

轮询

客户端定期向服务器发送请求 就是客户端定期向服务器发送请求,询问是否有新的数据可以用,服务器在接收到请求后,再检查是否有更新的数据,并将其返回给客户端 弊端:会明显产生大量的请求和响应,导致不必要的网络开销和延迟

长轮询

在客户端发出请求后,保持连接打开,等待新数据响应后在关闭连接, 在轮询的技术上做了改进,在客户端发出请求后,服务器回保持连接打开一段时间,在有新的数据可用时立即响应,然后在关闭连接,解决了无效轮询的数量,但还是需要频繁的建立和关闭连接

常见的轮询方式分为轮询与长轮询,它们的区别如下图所示:

image.png

为了更加直观感受轮询与长轮询之间的区别,我们来看一下具体的代码:

image.png

这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而 HTTP 请求与响应可能会包含较长的头部,其中真正有效的数据可能只是很小的一部分,所以这样会消耗很多带宽资源。

Comet

保持长连接,在返回请求后继续保持连接打开 Comet和长轮询一样,都是基于HTTP的技术,和长轮询不同的事,它可以在返回请求后,继续保持连接打开,它的核心思想就是通过保持长连接,来模拟实时通信并允许服务器通过流式传输frame等推送技术,来主动向客户端推送数据,不同Comet虽然可以模拟实时通信,但它仍然是基于HTTP的模型,在Comet中,服务器推送数据给客户端的方式通常还是通过延长响应或使用推送技巧来实现的

总的来说WebSocket相比起其他的技术的优势

实时通信:WebSocket允许在单个长时间的连接上进行双向实时通讯,在需要快速实时更新的应用程序里,WebSocket会比HTTP更加高效,降低延迟,WebSocket的连接一旦建立便会保持开放,数据可以在客户端和服务器之间,以比HTTP更低的延迟进行传输,更高效的资源利用,WebSocket可以减少重复请求和响应的开销,因为它连接只需要建立一次,可以说WebSocket的出现,填补了传统的HTTP协议,在实时通信方面的不足,它允许客户端和服务器之间,通过单个TCP连接进行双工通信,并且进行实时的数据交换,所以,WebSocket的协议非常实用与基于Web的游戏,聊天应用以及任何需要低延迟,实时连接的应用程序,目前的WebSocket一句得到了主流浏览器的支持,在js,c,python java都有相应的库,来支持WebSocket的实现和使用。

①实现一个vue+node的demo

准备工作

首先,我们需要安装 Node.js 和 Vue。如果你已经拥有这两者中的一个或两者都拥有,可以跳过这一步。

  • 如果你没有安装 Node.js,请前往官网下载最新版本并完成安装:nodejs.org/en/download…
  • 如果你没有安装 Vue,请使用下面的命令进行安装:
npm install -g @vue/cli

创建 Vue 的项目

创建 Vue 项目很简单。在终端运行以下命令:

bash
复制代码
vue create my-project

这会自动为你创建一个基本的 Vue 项目。

启动 Node.js 服务器

在项目根目录下创建一个名为 server.js 的文件,以便启动 Node.js 服务器。使用以下命令来安装所需的依赖项:

bash
复制代码
npm install ws express cors

在 server.js 文件中输入以下代码:

const WebSocket = require('ws');
const express = require('express');
const cors = require('cors');

const app = express();

app.use(cors());

const server = app.listen(8080, () => {
  console.log('Server started on port 8080');
});

const wss = new WebSocket.Server({ server });

wss.on('connection', (ws) => {
  ws.on('message', (message) => {
    console.log(`Received message => ${message}`);
    wss.clients.forEach((client) => {
      if (client !== ws && client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });
});

上述代码启动了一个监听 8080 端口的 Express 服务器,使用 WebSocket 启动了消息推送功能。

要启动 Node.js 服务器,请在终端运行以下命令:

node server.js

在 Vue 中使用 WebSocket 实现通讯

在 Vue 项目的 src 文件夹中,创建一个名为 Message.vue 的组件。此组件将用于处理 WebSocket 消息:

<template>
  <div>
    <h2>Messages</h2>
    <ul>
      <li v-for="message in messages" :key="message.id">
        {{ message.text }}
      </li>
    </ul>
    <hr>
    <input type="text" v-model="inputMessage">
    <button @click="sendMessage()">Send</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      messages: [],
      inputMessage: '',
    };
  },
  mounted() {
    this.websocket = new WebSocket('ws://localhost:8080');
    this.websocket.onmessage = this.handleMessage;
  },
  methods: {
    handleMessage(event) {
      const message = JSON.parse(event.data);
      this.messages.push(message);
    },
    sendMessage() {
      const message = {
        id: Date.now(),
        text: this.inputMessage,
      };
      this.websocket.send(JSON.stringify(message));
      this.inputMessage = '';
    },
  },
};
</script>

Message.vue 组件中,我们向 WebSocket 发送了一条消息,并在收到消息时更新了消息列表。我们还为用户提供了一个输入框和发送按钮,以便输入和发送新的消息。

②实现一个使用浏览器 + websocket + node 做一个简单的聊天室

Ⅰ. 服务端

  1. 第一步:创建一个空文件夹,然后使用 npm init -y 初始化整个文件夹

  2. 安装 websocket:npm install websocket,websocket 的 npm 包的网址:www.npmjs.com/package/web…

  3. 在目录中创建一个文件,命名为 server.js

    在这里插入图片描述

  4. 首先需要引入 websocket,很重要的一点:需要再引入 http 模块,因为 websocket 是基于 http 协议的

    const  websocket = require('websocket').server
    
    const http = require('http')
    
  5. 然后启动 http,开启服务器,进行监听

    const httpServer = http.createServer().listen(8080, ()=>{
            console.log('nihao: ','http://localhost:8080');
    })
    
  6. 创建 websocket 服务,这里面有两个必填的参数

    const websocketServer = new websocket({
            httpServer: httpServer,
            autoAcceptConnections: false
    })
    
  7. 创建通信线池,如果不适使用数据库的话,需要用数组来做一个虚拟的线池,因为 websocket 是一个实时通信的,服务器与客户端之间的通信是有来有回的,需要将这些通信的数据进行存储。一般使用 redis 来做线池,然后上限存储的数据数量一般是 1000 条

    const conArr = []
    
  8. 使用 websocket 来接受请求和发送消息,这里用到的是 websocket 的 npm 包中的 .on('requset', function(){}) 方法和 .on('message', function() {} 方法。这些方法的使用都可以从 websocket 的 npm 文档中查看,或者是从 github 上面查看

    websocketServer.on('request', function(request) {
    	// 这就是一次客户端发送的消息
    	// websocket 需要将这个链接保存起来
    	// 只要客户端和服务器没有断开,这个链接必须在
     	// 客户端与服务端的通信都是从这个链接上通信
    	const connection = request.accept()
    
    	// 每次接收一个链接,将它存放在数组里面
    	conArr.push(connection)
    
    	// 监听客户端发送的消息
    	connection.on('message', function(message) {
        	console.log(message);
     		// 发送消息给客户端(广播到各个客户端)
      		// 后面加上 utf8Data 编码
     		// 要将所有的链接全部发出去,才可以让每个客户端接收到其他传来的数据
      		for(let i = 0; i < conArr.length; i++) {
       			conArr[i].send(message.utf8Data)
       		}
    	})
    })
    
  9. 最后启动服务器就可以:node server.js在这里插入图片描述

Ⅱ. 客户端

  1. 在文件夹中创建一个 index.html 文件,先编写一个简陋的 HTML 页面

    <!-- 需要一进来如浏览器就要建立链接 -->
    <!-- 点击按钮发送消息给服务器 -->
    输入姓名:<input type="text" id="uName">
    <br>
    输入消息:<input type="text" id="context">
    
    <button id="btn"> 点击发送消息 </button>
    
    <div id="charRoom"></div>
    
  2. 先通过 js 获取到页面中的节点,都时候通过这些节点进行一系列的操作

    // 用户名
    const uName = document.getElementById('uName')
    // 文本框内容
    const context = document.getElementById('context')
    // 点击按钮
    const btn = document.getElementById('btn')
    // 要显示聊天室的区域
    const charRoom = document.getElementById('charRoom')
    
  3. 注意,因为在浏览器中自带 websocket,所以直接通过 new 实例化 websocket 对象就行,并不需要下载或者引入什么东西。 这里会用到浏览器中 websocket 自己的方法:websocket.onopen,websocket 的各种方法都可以从 MDN 文档中查看到:developer.mozilla.org/zh-CN/docs/…

    // 实例化 websocket
    // 必须加 'ws://localhost:8080' ws 协议,后面是开启的服务端接口
    const websocket = new WebSocket('ws://localhost:8080')
    // 打开事件
    websocket.onopen = function() {
      // 获取当前链接的状态
      // 1 是建立了链接
      console.log(websocket.readyState);
    }
    
  4. 创建一个按钮点击事件,通过这个事件通过 websocket 向服务器发送数据,向服务器发送数据需要通过 websocket 的 websocket.onmessage 方法进行消息的传送,这个方法有一个参数,参数就是自己要发送的 data 数据。但是注意,因为自己从页面中获取到的数据都不是 JSON 对象格式,在发送数据之前,需要将数据转换为 JSON 对象格式。

    // 点击发送消息的事件
    btn.onclick = function() {
      // 将用户名和要发送的内容放在一个对象中,一起传送给后端
      const values = {
        uName: uName.value,
        context: context.value
      }
    
      // 清空文本框的内容
      uName.value = ''
      context.value = ''
    
      // 通过 websockte 发送消息
      websocket.send(JSON.stringify(values))
    }
    
  5. 最后是让浏览器实时监听服务器传递过来的消息,需要通过 websocket 的 websocket.onmessage 方法获取服务器发送的消息。这里我通过模板字符串的形式,对文本内容进行拼接处理。

    // 接收服务器返回的消息
    websocket.onmessage = function(data) {
      // 服务器返回过来的聊天信息
      const chatS = JSON.parse(data.data)
    
      // 添加到页面上
      charRoom.innerHTML += `
        <strong>${chatS.uName}:</strong>
        <span>${chatS.context}</span>
        <br />
      `
    }
    

Ⅲ. 效果查看

       这个时候打开浏览器,就可以实现客户端之间的实时通信了。因为要查看客户端之间的实时通信,那么可以打开多个浏览器来查看效果:请添加图片描述        好了使用原生的 websocket + node 实现一个简单的聊天室就结束了。

websocket的用途场景

高即时性服务,比如聊天室的群聊,server顺序收到了张三,李四的消息,立即就推送给王五,不能让王五等半天。
Ajax也可以一秒一刷,让王五去问张三说话没,如果张三10分钟没说话,王五要去问600次。 用websocket 王五不用刷,等着就好了,服务器有了消息会自动给王五的。

1.websocket社交订阅
对社交类的应用的一个裨益之处就是能够即时的知道你的朋友正在做什么。虽然听起来有点可怕,但是我们都喜欢这样做。你不会想要在数分钟之后才能知道一个家庭成员在馅饼制作大赛获胜或者一个朋友订婚的消息。你是在线的,所以你的订阅的更新应该是实时的。

2.websocket多玩家游戏

网络正在迅速转变为游戏平台。在不使用插件(我指的是Flash)的情况下,网络开发者现在可以在浏览器中实现和体验高性能的游戏。无论你是在处理DOM元素、css动画,html5的canvas或者尝试使用WebGL,玩家之间的互动效率是至关重要的。我不想在我扣动扳机之后,我的对手却已经移动位置。

3.websocket协同编辑/编程

我们生活在分布式开发团队的时代。平时使用一个文档的副本就满足工作需求了,但是你最终需要有一个方式来合并所有的编辑副本。版本控制系统,比如Git能够帮助处理某些文件,但是当Git发现一个它不能解决的冲突时,你仍然需要去跟踪人们的修改历史。通过一个协同解决方案,比如WebSocket,我们能够工作在同一个文档,从而省去所有的合并版本。这样会很容易看出谁在编辑什么或者你在和谁同时在修改文档的同一部分。

用户打开文档编辑页面,与后端建立长连接。
后端将当前用户加入当前文档编辑用户列表。
前端监听用户对于文档内容的修改,每一次修改将整个修改内容发送给后端。
后端接收到信息,不做任何处理,直接将文本信息发送给文档编辑用户列表中其他的所有用户。
前端收到后端的文本信息直接覆盖掉当前文档内容。

在这里插入图片描述

4.websocket收集点击流数据

分析用户与你网站的互动是提升你的网站的关键。HTTP的开销让我们只能优先考虑和收集最重要的数据部分。然后,经过六个月的线下分析,我们意识到我们应该收集一个不同的判断标准——一个看起来不是那么重要但是现在却影响了一个关键的决定。与HTTP请求的开销方式相比,使用Websocket,你可以由客户端发送不受限制的数据。想要在除页面加载之外跟踪鼠标的移动?只需要通过WebSocket连接发送这些数据到服务器,并存储在你喜欢的NoSQL数据库中就可以了(MongoDB是适合记录这样的事件的)。现在你可以通过回放用户在页面的动作来清楚的知道发生了什么。

5.股票基金报价

金融界瞬息万变——几乎是每毫秒都在变化。我们人类的大脑不能持续以那样的速度处理那么多的数据,所以我们写了一些算法来帮我们处理这些事情。虽然你不一定是在处理高频的交易,但是,过时的信息也只能导致损失。当你有一个显示盘来跟踪你感兴趣的公司时,你肯定想要随时知道他们的价值,而不是10秒前的数据。使用WebSocket可以流式更新这些数据变化而不需要等待。

6.体育实况更新

现在我们开始讨论一个让人们激情澎湃的愚蠢的东西——体育。我不是运动爱好者,但是我知道运动迷们想要什么。当爱国者在打比赛的时候,我的妹夫将会沉浸于这场比赛中而不能自拔。那是一种疯狂痴迷的状态,完全发自内心的。我虽然不理解这个,但是我敬佩他们与运动之间的这种强烈的联系,所以,最后我能做的就是给他的体验中降低延迟。如果你在你的网站应用中包含了体育新闻,WebSocket能够助力你的用户获得实时的更新。

7.多媒体聊天

视频会议并不能代替和真人相见,但当你不能在同一个屋子里见到你谈话的对象时,视频会议是个不错的选择。尽管视频会议私有化做的“不错”,但其使用还是很繁琐。我可是开放式网络的粉丝,所以用WebSockets getUserMedia API和html5音视频元素明显是个不错的选择。WebRTC的出现顺理成章的成为我刚才概括的组合体,它看起来很有希望,但其缺乏目前浏览器的支持,所以就取消了它成为候选人的资格。

8.基于位置的应用

越来越多的开发者借用移动设备的GPS功能来实现他们基于位置的网络应用。如果你一直记录用户的位置(比如运行应用来记录运动轨迹),你可以收集到更加细致化的数据。如果你想实时的更新网络数据仪表盘(可以说是一个监视运动员的教练),HTTP协议显得有些笨拙。借用WebSocket TCP链接可以让数据飞起来。