webSocket是一项可让服务器将数据主动推送给客户端的技术。前几天写了一个日志功能,日志数据须要实时更新。正好项目中有封装好的WebSocket组件,且接口支持webSocket,就用它实现了。也是第一次用,简单研究了一下,分享出来。
文章示例代码:https://github.com/neroneroff...html
首先须要明白webSocket的概念,下边是维基百科的解释前端
WebSocket是一种通讯协议,可在单个TCP链接上进行全双工通讯。WebSocket使得客户端和服务器之间的数据交换变得更加简单,
容许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只须要完成一次握手,二者之间就能够创建持久性的链接,
并进行双向数据传输。
首先,要明白WebSocket是一种通讯协议,区别于HTTP协议,HTTP协议只能实现客户端请求,服务端响应的这种单项通讯。
而WebSocket能够实现客户端与服务端的双向通信,说白了,最大也是最明显的区别就是能够作到服务端主动将消息推送给客户端。git
其他的特色有:github
咱们都知道,不使用WebSocket与服务器实时交互,通常有两种方法。AJAX轮询和Long Polling长轮询。web
AJAX轮询是定时发送请求,也就是普通的客户端与服务端通讯过程,只不过是无限循环发送,这样,能够保证服务端一旦有最新消息,就能够被客户端获取。express
Long Polling长轮询是客户端和浏览器保持一个长链接,等服务端有消息返回,断开。
而后再从新链接,也是个循环的过程,无穷尽也。。。npm
客户端发起一个Long Polling,服务端若是没有数据要返回的话,
会hold住请求,等到有数据,就会返回给客户端。客户端又会再次发起一次Long Polling,再重复一次上面的过程。后端
上边这两种方式都有个致命的弱点,开销太大,被动性。假设并发很高的话,这对服务端是个考验。
而WebSocket一次握手,持久链接,以及主动推送的特色能够解决上边的问题,又不至于损耗性能。浏览器
客户端发起HTTP握手,告诉服务端进行WebSocket协议通信,并告知WebSocket协议版本。服务端确认协议版本,升级为WebSocket协议。以后若是有数据须要推送,会主动推送给客户端。服务器
链接开始时,客户端使用HTTP协议和服务端升级协议,升级完成后,后续数据交换遵循WebSocket协议。咱们看看Request Headers
Accept-Encoding: gzip, deflate, br Accept-Language: zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6 Cache-Control: no-cache Connection: Upgrade Host: 127.0.0.1:3000 Origin: http://localhost:3000 Pragma: no-cache Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits Sec-WebSocket-Key: bwb9SFiJONXhQ/A4pLaXIg== Sec-WebSocket-Version: 13 Upgrade: websocket
重点字段是这些:
再看看看服务端响应的 Response Headers
Connection: Upgrade Sec-WebSocket-Accept: 2jrbCWSCPlzPtxarlGTp4Y8XD20= Upgrade: websocket
关键是这个字段
客户端若想要与支持webScoket的服务器通讯,可使用WebSocket构造函数返回WebSocket对象。
const ws = new WebSocket("ws://localhost:3000/websocket");
这样,客户端就会与服务端开始链接。
返回的实例对象的属性:
名称 | 值 |
---|---|
WebSocket.CONNECTING | 0 |
WebSocket.OPEN | 1 |
WebSocket.CLOSING | 2 |
WebSocket.CLOSED | 3 |
方法:
讲了那么多概念之后,终于能够看看怎么用了。实现WebSocket通讯,须要客户端和服务端配合。
本身写了一个例子,服务端在开始链接后,利用定时器主动向客户端发送随机数,客户端也能够发给服务器消息,
而后服务器返回这条消息给客户端。客户端就是js+html,服务端用了express + express-ws来实现。
代码在这里:https://github.com/neroneroff... 能够clone下来,安装依赖,npm start运行看下效果。
前端页面,最终效果如以上效果图:
<body> <div class="websocket"> <div class="receive"> <p>服务端返回的消息</p> <div id="receive-box"></div> </div> <div class="send"> <textarea type="text" id="msg-need-send"></textarea> <p> <button id="send-btn">点击发消息给服务端</button> </p> </div> <button id="exit">关闭链接</button> </div> </body>
js,使用webSocket的代码都在这里。作的事情就是给页面的元素绑定事件。
而后建立WebSocket对象,监听对象的链接、接收消息、关闭等事件,将数据反馈到页面中
const msgBox = document.getElementById("msg-need-send") const sendBtn = document.getElementById("send-btn") const exit = document.getElementById("exit") const receiveBox = document.getElementById("receive-box") // 建立一个webSocket对象 const ws = new WebSocket("ws://127.0.0.1:3000/websocket/test") ws.onopen = e => { // 链接后监听 console.log(`WebSocket 链接状态: ${ws.readyState}`) } ws.onmessage = data => { // 当服务端返回数据的时候,放到页面里 receiveBox.innerHTML += `<p>${data.data}</p>` receiveBox.scrollTo({ top: receiveBox.scrollHeight, behavior: "smooth" }) } ws.onclose = data => { // 监听链接关闭 console.log("WebSocket链接已关闭") console.log(data); } sendBtn.onclick = () => { // 点击发送按钮。将数据发送给服务端 ws.send(msgBox.value) } exit.onclick = () => { // 客户端主动关闭链接 ws.close() }
考虑到了模块化开发,没有直接把代码放到直接建立服务的文件中。而是使用了路由,给webSocket服务分配一个单独的接口
const express = require("express"); const expressWs = require("express-ws") const router = express.Router() expressWs(router); router.ws("/test", (ws, req) => { ws.send("链接成功") let interval // 链接成功后使用定时器定时向客户端发送数据,同时要注意定时器执行的时机,要在链接开启状态下才能够发送数据 interval = setInterval(() => { if (ws.readyState === ws.OPEN) { ws.send(Math.random().toFixed(2)) } else { clearInterval(interval) } }, 1000) // 监听客户端发来的数据,直接将信息原封不动返回回去 ws.on("message", msg => { ws.send(msg) }) }) module.exports = router
最后看一下数据交互的过程
上边简单实现了一个webSocket通讯。实际的东西还有不少,好比webSocket扩展,心跳检测,数据加密,身份认证等知识点。但本身也须要再去研究,因此先不作介绍了。
欢迎关注个人公众号: 一口一个前端,不按期分享我所理解的前端知识