你不知道WebSocket吗?
什么是WebSocket?
WebSocket是一种在单个TCP连接上进行全双工通信的协议。这里我们发现了一个有趣的词:”全双工”,那我们就来简单了解下通信方式有哪些!
单工
通信双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输。
例如计算机与打印机之间的通信是单工模式
说的简单些就是:我打你你只能忍着!
半双工
允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,其实际上是切换的单工。
例如HTTP协议:客户端向服务器发送请求(单向的),然后服务器响应请求(单向的)
说的简单些就是:我打你,你忍完后可以打我,我忍着…
全双工
允许数据在两个方向上同时传输。
例如手机通话,WebSocket就是这个样子!
说的简单些就是:两个人同时可以互相打对方
说了这么多其实目的就是让大家知道,WebSocket是支持双向通信的!
双向通信的优点
为什么要支持双向通信?单向通信有什么问题?还是从HTTP说起,我们知道HTTP协议是半双工的,而且服务器不能主动推送消息给浏览器!这个就是他的缺陷。假若我希望实现一个股票交易系统,可能股价每秒钟都有变化,但是价格变化了如何通知我们的客户端?
咱们来看看以前是怎么实现的!
轮询
什么叫轮询?就是不停的轮番询问!说的直白些就是客户端定期发送请求给服务端。
短轮询
配段代码,Talk is cheap,show me your code.
const express = require("express"); const app = express(); // express 静态服务中间件用来返回静态文件 app.use(express.static(__dirname)); // 当前价格是100元 let currentPrice = 100; // 获取最新价格接口 app.get("/getPrice", (req, res, next) => { res.send('¥'+currentPrice * Math.random()); }); app.listen(3000);
客户端不停的发送请求,去服务端获取最新价格。
<div>当前交易价格: <span id="price"></span></div> <script> setInterval(() => { fetch('/getPrice'). then(res=>res.text()). then(data=>price.innerHTML = data) }, 1000); </script>
很快我们就看出了这样编写代码的缺陷!如果数据变化的不快呢,那就会发送很多无意义的请求。每次发送请求都会有HTTP的Header会消耗大量流量,同时也会消耗CPU的利用率!
长轮询
长轮询是对短轮询的改进版,就是当第一个请求回来时再发送下一个请求!
(function poll(){ fetch('/getPrice'). then(res=>res.text()). then(data=>{price.innerHTML = data;poll()}) })()
问题依旧是显而易见的!如果服务端数据变化很快,那么请求数目会更多;如果变化很慢,可能ajax会出现超时的问题。
Iframe方式
我们并不希望每次都创建一个新的请求,此时就可以使用Iframe来实现长连接
app.get("/getPrice", (req, res, next) => { setInterval(()=>{ // 不能使用end 否则会中断请求,我们要实现的是长连接 res.write(` <script> parent.document.getElementById('price').innerHTML = ${currentPrice * Math.random()} </script> `); },1000); });
<body> <div>当前交易价格: <span id="price"></span></div> <iframe src="/getPrice" frameborder="0"></iframe> </body>
现在确实可以利用Iframe实现了长连接通信,但是页面的状态一直是加载态!
EventSource流
EventSource 接口用于接收服务器发送的事件。它通过HTTP连接到一个服务器,以text/event-stream 格式接收事件, 不关闭连接。
<div>当前交易价格: <span id="price"></span></div> <script> const eventSource = new EventSource('/getPrice'); eventSource.onmessage = function(e){ // 拿到接受到的数据 price.innerHTML = e.data; } </script>
app.get("/getPrice", (req, res, next) => { res.header('Content-Type','text/event-stream',); timer = setInterval(()=>{ res.write( // 发送message事件 \n\n表示当前的event-stream通信结束 `event:message\nid:${id++}\ndata:${currentPrice*Math.random()}\n\n` ); },1000); res.on('close',()=>{ clearInterval(timer); }); });
当然这种方式依旧是单向的,主要是服务端向客户端推送数据。并且兼容性也不是很美丽~
WebSocket
终于等到你! 双向通信的WebSocket让你欲罢不能!
WebSocket让客户端和服务器保有一个持久的连接,两边可以在任意时间开始发送数据!它是基于TCP协议的:
先来聊聊WebSocket的优势!
- http协议不支持双向通信 -> 我支持双向通信
- http协议数据包头部较大 -> 我的header很小!我最少只需两个字节
- http不支持跨域 -> 我支持跨域,哈哈!
ws模块
ws: a Node.js WebSocket library,ok就是在node中可以使用的WebSocket库!
安装ws模块
yarn add ws
服务端开启WebSocket服务
const WebSocketServer = require('ws').Server; const ws = new WebSocketServer({port:8888}); ws.on('connection',(socket)=>{ // socket链接我的那个人 console.log('服务端:有人链接我!'); socket.on('message',(data)=>{ console.log(data); // 收到客户端发来的消息 socket.send('我是服务端'); // 给客户端发消息 }); });
客户端链接8888端口的ws服务!
const socket = new WebSocket('ws://localhost:8888'); socket.onopen = function(){ // 链接成功后,发送消息 console.log('客户端:链接成功'); socket.send('我是客户端'); } socket.onmessage = function(e){ // 监听客户端发来的信息 console.log(e.data); }
客户端和服务端可以开心的互相通信啦!
socket.io
socket.io是一个WebSocket库,包括了客户端的js和服务器端的nodejs,刚才是不是高兴的太早了而忘记了兼容性问题?没错socket.io就是帮你解决自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用!
安装socket.io模块
yarn add socket.io
通过socket.io建立链接
const express = require("express"); const app = express(); app.use(express.static(__dirname)) const server = require('http').createServer(app); // app本身就是监听函数 // socket 需要借助http服务 const io = require('socket.io')(server); // 划分路径 / io.of('/').on('connection',function(socket){ console.log('链接成功') socket.on('message',function(msg){ console.log(msg); socket.send('我是服务端'); }); }); // 监听3000 端口 server.listen(3000);
// 默认会像浏览器中注入socket.io.js脚本 <script src="/socket.io/socket.io.js"></script> <script> const socket = io.connect('/'); socket.on('connect',()=>{ console.log('链接成功'); socket.send('我是客户端'); }); // 接收到消息后打印出来 socket.on('message',(data)=>{ console.log(data); }); </script>
我们有了socket.io实现双向通信是不是很简单!
觉得本文对你有帮助吗?请分享给更多人
关注「前端优选」加星标,提升前端技能
关注公众号,获得更多前端高级技能
加我微信:webyouxuan