随着 Web 的发展,用户对于 Web 的实时推送要求也愈来愈高 ,好比,工业运行监控、Web 在线通信、即时报价系统、在线游戏等,都须要将后台发生的变化主动地、实时地传送到浏览器端,而不须要用户手动地刷新页面。本文对过去和如今流行的 Web 实时推送技术进行了比较与总结。javascript
本文完整的源代码请猛戳Github博客,纸上得来终觉浅,建议你们动手敲敲代码。html
HTTP 协议有一个缺陷:通讯只能由客户端发起。举例来讲,咱们想了解今天的天气,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议作不到服务器主动向客户端推送信息。这种单向请求的特色,注定了若是服务器有连续的状态变化,客户端要获知就很是麻烦。在WebSocket协议以前,有三种实现双向通讯的方式:轮询(polling)、长轮询(long-polling)和iframe流(streaming)。前端
// 1.html
<div id="clock"></div>
<script>
let clockDiv = document.getElementById('clock');
setInterval(function(){
let xhr = new XMLHttpRequest;
xhr.open('GET','/clock',true);
xhr.onreadystatechange = function(){
if(xhr.readyState == 4 && xhr.status == 200){
console.log(xhr.responseText);
clockDiv.innerHTML = xhr.responseText;
}
}
xhr.send();
},1000);
</script>
复制代码
//轮询 服务端
let express = require('express');
let app = express();
app.use(express.static(__dirname));
app.get('/clock',function(req,res){
res.end(new Date().toLocaleString());
});
app.listen(8080);
复制代码
启动本地服务,打开http://localhost:8080/1.html
,获得以下结果:java
// 2.html 服务端代码同上
<div id="clock"></div>
<script>
let clockDiv = document.getElementById('clock')
function send() {
let xhr = new XMLHttpRequest()
xhr.open('GET', '/clock', true)
xhr.timeout = 2000 // 超时时间,单位是毫秒
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
//若是返回成功了,则显示结果
clockDiv.innerHTML = xhr.responseText
}
send() //无论成功仍是失败都会发下一次请求
}
}
xhr.ontimeout = function() {
send()
}
xhr.send()
}
send()
</script>
复制代码
// 3.html
<body>
<div id="clock"></div>
<iframe src="/clock" style="display:none"></iframe>
</body>
复制代码
//iframe流
let express = require('express')
let app = express()
app.use(express.static(__dirname))
app.get('/clock', function(req, res) {
setInterval(function() {
let date = new Date().toLocaleString()
res.write(`
<script type="text/javascript">
parent.document.getElementById('clock').innerHTML = "${date}";//改变父窗口dom元素
</script>
`)
}, 1000)
})
app.listen(8080)
复制代码
启动本地服务,打开http://localhost:8080/3.html
,获得以下结果: git
上述代码中,客户端只请求一次,然而服务端倒是源源不断向客户端发送数据,这样服务器维护一个长链接会增长开销。github
以上咱们介绍了三种实时推送技术,然而各自的缺点很明显,使用起来并不理想,接下来咱们着重介绍另外一种技术--websocket,它是比较理想的双向通讯技术。web
WebSocket是一种全新的协议,随着HTML5草案的不断完善,愈来愈多的现代浏览器开始全面支持WebSocket技术了,它将TCP的Socket(套接字)应用在了webpage上,从而使通讯双方创建起一个保持在活动状态链接通道。chrome
一旦Web服务器与客户端之间创建起WebSocket协议的通讯链接,以后全部的通讯都依靠这个专用协议进行。通讯过程当中可互相发送JSON、XML、HTML或图片等任意格式的数据。因为是创建在HTTP基础上的协议,所以链接的发起方还是客户端,而一旦确立WebSocket通讯链接,不论服务器仍是客户端,任意一方均可直接向对方发送报文。express
初次接触 WebSocket 的人,都会问一样的问题:咱们已经有了 HTTP 协议,为何还须要另外一个协议?跨域
相对于传统的HTTP每次请求-应答都须要客户端与服务端创建链接的模式,WebSocket是相似Socket的TCP长链接的通信模式,一旦WebSocket链接创建后,后续数据都以帧序列的形式传输。在客户端断开WebSocket链接或Server端断掉链接前,不须要客户端和服务端从新发起链接请求。在海量并发和客户端与服务器交互负载流量大的状况下,极大的节省了网络带宽资源的消耗,有明显的性能优点,且客户端发送和接受消息是在同一个持久链接上发起,实时性优点明显。
接下来我看下websocket如何实现客户端与服务端双向通讯:
// websocket.html
<div id="clock"></div>
<script>
let clockDiv = document.getElementById('clock')
let socket = new WebSocket('ws://localhost:9999')
//当链接成功以后就会执行回调函数
socket.onopen = function() {
console.log('客户端链接成功')
//再向服务 器发送一个消息
socket.send('hello') //客户端发的消息内容 为hello
}
//绑定事件是用加属性的方式
socket.onmessage = function(event) {
clockDiv.innerHTML = event.data
console.log('收到服务器端的响应', event.data)
}
</script>
复制代码
// websocket.js
let express = require('express')
let app = express()
app.use(express.static(__dirname))
//http服务器
app.listen(3000)
let WebSocketServer = require('ws').Server
//用ws模块启动一个websocket服务器,监听了9999端口
let wsServer = new WebSocketServer({ port: 9999 })
//监听客户端的链接请求 当客户端链接服务器的时候,就会触发connection事件
//socket表明一个客户端,不是全部客户端共享的,而是每一个客户端都有一个socket
wsServer.on('connection', function(socket) {
//每个socket都有一个惟一的ID属性
console.log(socket)
console.log('客户端链接成功')
//监听对方发过来的消息
socket.on('message', function(message) {
console.log('接收到客户端的消息', message)
socket.send('服务器回应:' + message)
})
})
复制代码
启动本地服务,打开http://localhost:3000/websocket.html
,获得以下结果:
方式 | 类型 | 技术实现 | 优势 | 缺点 | 适用场景 |
---|---|---|---|---|---|
轮询Polling | client→server | 客户端循环请求 | 一、实现简单 二、 支持跨域 | 一、浪费带宽和服务器资源 二、 一次请求信息大半是无用(完整http头信息) 三、有延迟 四、大部分无效请求 | 适于小型应用 |
长轮询Long-Polling | client→server | 服务器hold住链接,一直到有数据或者超时才返回,减小重复请求次数 | 一、实现简单 二、不会频繁发请求 三、节省流量 四、延迟低 | 一、服务器hold住链接,会消耗资源 二、一次请求信息大半是无用 | WebQQ、Hi网页版、Facebook IM |
长链接iframe | client→server | 在页面里嵌入一个隐蔵iframe,将这个 iframe 的 src 属性设为对一个长链接的请求,服务器端就能源源不断地往客户端输入数据。 | 一、数据实时送达 二、不发无用请求,一次连接,屡次“推送” | 一、服务器增长开销 二、没法准确知道链接状态 三、IE、chrome等一直会处于loading状态 | Gmail聊天 |
WebSocket | server⇌client | new WebSocket() | 一、支持双向通讯,实时性更强 二、可发送二进制文件三、减小通讯量 | 一、浏览器支持程度不一致 二、不支持断开重连 | 网络游戏、银行交互和支付 |
综上所述:Websocket协议不只解决了HTTP协议中服务端的被动性,即通讯只能由客户端发起,也解决了数据同步有延迟的问题,同时还带来了明显的性能优点,因此websocket 是Web 实时推送技术的比较理想的方案,但若是要兼容低版本浏览器,能够考虑用轮询来实现。
给你们推荐一个好用的BUG监控工具Fundebug,欢迎免费试用!
欢迎关注公众号:前端工匠,你的成长咱们一块儿见证!若是你感受有收获,欢迎给我打赏,以激励我更多输出优质开源内容