WebSocket是一种在单个TCP链接上进行全双工通讯的协议。这里咱们发现了一个有趣的词:”全双工”,那咱们就来简单了解下通讯方式有哪些!javascript
通讯双方中,一方固定为发送端,一方则固定为接收端。信息只能沿一个方向传输。 例如计算机与打印机之间的通讯是单工模式html
说的简单些就是:我打你你只能忍着!前端
容许数据在两个方向上传输,可是同一时间数据只能在一个方向上传输,其其实是切换的单工。 例如HTTP协议:客户端向服务器发送请求(单向的),而后服务器响应请求(单向的)java
说的简单些就是:我打你,你忍完后能够打我,我忍着…node
容许数据在两个方向上同时传输。 例如手机通话,WebSocket就是这个样子!web
说的简单些就是:两我的同时能够互相打对方ajax
说了这么多其实目的就是让你们知道,WebSocket是支持双向通讯的!express
为何要支持双向通讯?单向通讯有什么问题?仍是从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来实现长链接
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 接口用于接收服务器发送的事件。它经过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的优点!
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是一个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