简单介绍 websocket
WebSocket是一种在TCP连接上进行全双工通信的协议。 WebSocket通信协议于2011年被IETF定为标准。 然后WebSocket API也被W3C定为标准。 WebSocket使得客户端和服务器之间的数据交换变得更加简单, 允许服务端主动向客户端推送数据。 在WebSocket API中,浏览器和服务器只需要完成一次握手。 两者之间就直接可以创建持久性的连接,并进行双向数据传输。
我们为什么要使用 websocket
我们都知道 HTTP 协议有一个缺陷。 那就是通信只能由客户端发起。服务端没法主动发起。 有些场景我们需要让客户端实时知道消息。 虽然可以使用轮询来解决这个问题。 但是非常浪费资源,因为要不停连接,断开后又继续连接。 这个时候我们就需要使用 websocket
websocket 应用场景
1.弹幕媒体聊天 [客服推送] 2.协同编辑 [wps] 3.基于位置的应用 [外卖] 4.体育实况更新 5.股票帮金报价实时更新
websocket 和HTTP请求的不同点:
1.WebSocket是双向通信协议,HTTP是单向的 WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。而HTTP是单向的 2.连接方式不同 WebSocket是需要浏览器和服务器握手进行建立连接的。 http是浏览器发起向服务器的连接,服务器预先并不知道这个连接。 3.协议开头不同 websocket:websocket的协议是以 ws/wss 开头。 http 对应的是 http/https 4.连接长度不同 websocket是持久连接。http 是短连接。
简单实现一下 WebSocket
node创建WebSocket
我们需要先下载 npm install ws ws是一个简单易用、快速且经过全面测试的WebSocket客户端和服务器实现。 我们就直接使用就好了
const express = require('express') const app = express() const port = 3000 // 托管静态资源-express.static() app.use(express.static('public')) app.get('/', (req, res) => res.send('Hello World!')) app.listen(port, () => console.log(`Example app listening on port ${port}!`)) // 引入 ws,并且解构使用我们需要的 WebSocketServer const { WebSocketServer } = require('ws'); // 创建WebSocketServer的实例wss,并指定端口号 const wss = new WebSocketServer({ port: 8080 }); // 通过实例wss进行监听 wss.on('connection', function connection(ws) { ws.on('error', console.error); // 接收客户端发送过来的消息 ws.on('message', function message(data) { console.log('客户端发送过来的消息', data); }); ws.send('服务端发送的消息'); });
客户端 WebSocket 的创建
我们需要创建 WebSocket 的实例同时指定端口号。 然后通过 onopen onmessage onerror 来判断ws的状态事件 下面我们就来简单写一下
<body> <h1>欢迎 学习WebSocket </h1> </body> <script> // 创建 WebSocket并指定一个地址 let ws = new WebSocket('ws://localhost:8080') // 是否链接成功 ws.onopen = (() => { console.log("链接成功") }) // 监听服务端发送过来的消息 ws.onmessage = ((messObj) => { console.log('发送过来的消息', messObj) }) // 连接失败 ws.onerror = (() => { console.log('连接失败') }) </script>
客户端发送消息给服务端
我们需要写一个简单的html界面, 获取到 文本域中的内容。 然后通过 ws.send(textareaValue) 给服务端发送消息。 服务端在 ws.on('message', function message(data) { console.log(data) }) 这个方法中接收客户端的消息 data就是客户端发送过来的消息 如果是二进制数据,我们可以使用 data.toString()进行转换
<body> <h1>欢迎 学习WebSocket </h1> <textarea rows="10" cols="30" id="textarea" placeholder="请输入内容"></textarea> <button onclick="sendHandler()">点击按钮</button> </body> // 创建 WebSocket并指定一个地址 let ws = new WebSocket('ws://localhost:8080') let textarea=document.getElementById('textarea') function sendHandler(){ const textareaValue = textarea.value; // 通过ws.send给客户端发送消息 ws.send(textareaValue) }
WebSocket 实现广播
我们要做一个广播。 就有点类似在QQ群中发送一条消息, 在这个群中的其他用户可以接收到这条消息。 WebSocket 连接成功后,wss.clients来获取当前服务端连接了多少个客户端。 wss.clients是一个Set类型的。我们通过 forEach 来进行遍历。 我们通过 client.readyState 的状态是否是 WebSocket.OPEN 然后调用 client.send(data, { binary:false })就数据发送出去
// 引入 ws,并且解构使用我们需要的 WebSocketServer const { WebSocketServer, WebSocket } = require('ws'); // import { WebSocketServer } from 'ws'; // 创建WebSocketServer的实例wss,并指定端口号 const wss = new WebSocketServer({ port: 8080 }); // 通过实例wss进行监听 wss.on('connection', function connection(ws) { ws.on('error', console.error); // 客户端 ws.on('message', function message(data) { console.log('received: %s', data); // 服务器广播 // 广播到所有连接的WebSocket客户端,包括其自身。 console.log('连接了多少个',wss.clients.size) wss.clients.forEach(function each(client) { // 判断当前连接服务端的所有客户端的ws。 if (client.readyState === WebSocket.OPEN) { // binary表示数据是否是二进制。binary:false表示不是二进制 client.send(data, { binary:false }); } }); }); ws.send('服务端发送的消息'); });
发现自己也被广播了
假设不同的浏览器代码一个用户。 通过上面的图片,我们发现谷歌浏览器发送一条消息。 虽然火狐浏览器接收到了。但是谷歌发送的消息自己也接收到了。 这样明显是不正确的。因此。我们处理一下。 自己发送的消息不需要广播自己的。 我们使用 client!==ws 来进行判断
// 引入 ws,并且解构使用我们需要的 WebSocketServer const { WebSocketServer, WebSocket } = require('ws'); // import { WebSocketServer } from 'ws'; // 创建WebSocketServer的实例wss,并指定端口号 const wss = new WebSocketServer({ port: 8080 }); // 通过实例wss进行监听 wss.on('connection', function connection(ws) { ws.on('error', console.error); // 客户端 ws.on('message', function message(data) { console.log('received: %s', data); // 服务器广播 // 广播到所有连接的WebSocket客户端,包括其自身。 console.log('连接了多少个',wss.clients.size) wss.clients.forEach(function each(client) { // 判断当前连接服务端的所有客户端的ws用户。 // client!==ws 表示不是当前发送的用户[不要自己广播自己] if (client!==ws && client.readyState === WebSocket.OPEN) { // binary表示数据是否是二进制。binary:false表示不是二进制 client.send(data, { binary:false }); } }); }); // 第一次连接成功后发送的消息。 ws.send(''); });
实现关闭页面关闭 WebSocket
当我们离开连接 WebSocket的页面离开后, 我们应该关闭当前连接。 可是现在我们点击去user页面的时候,并没有关闭连接。 这显然是不太好的。所以我们需要优化一下。 当用户离开,我们需要关闭连接
// 关闭当前窗口,按下 F5 或点击以下链接触发 onbeforeunload 事件 document.body.onbeforeunload = function () { ws.close() return 'close' } //离开当前页面会触发 window.onunload = function (e) { var e = window.event || e; console.log('===>',e) // 离开页面,关闭WebSocket ws.close() }
携带token
我们在连接 WebSocket 的时候, 没有鉴权,也就是说无法判断当前这个用户的信息。 这样是不安全的。 因此我们在连接 WebSocket 需要携带token. 通过这样的形式携带 ws://localhost:8080?token=' + 'tokne_zahngsan'
// 创建 WebSocket并指定一个地址,并且携带一个token let ws = new WebSocket('ws://localhost:8080?token=' + 'tokne_zahngsan') //省略其他代码
wss.on('connection', function connection(ws,req) { // 获取前端携带的token console.log('req', req.url.slice(8)) })