轮询、服务器发送事件与WebSocket
轮询
传统轮询
传统轮询借助setInterval
或者setTimeout
,并结合Ajax技术的方式实现。
//1
//使用setInterval,每隔10s向服务器发送一次请求。
//存在的问题是:在网络不稳定的情况下,无法保证请求以及服务器响应的顺序。
setInterval(function() {
$.getJSON("url-path")
.done(function(data) {
console.log(data);
});
}, 10000);
//2
//使用setTimeout,在每一次的请求并收到响应后,发送下一次请求。
//解决的问题:保证了每次请求的先后顺序。
//存在的问题:仍然无法保证每次请求的间隔时间。
function poll() {
setTimeout(function() {
$.getJSON("url-path")
.done(function(data) {
console.log(data);
//发起下一次请求
poll();
});
}, 1000);
}
缺点分析
每次都需要新发起一条http请求。
对客户端来说占用较多内存资源与请求资源,对服务器来说占用较多的内存资源与带宽资源。
补充:对TCP协议三次握手的理解。
请求(含SYN标记的数据包):客户端-->Apache/nginx服务器-->php/java/...等后端程序
响应(含SYN-ACK标记的数据包):后端程序-->Apache/nginx服务器-->客户端
确认收到(含ACK标记的数据包):客户端-->Apache/nginx服务器-->后端程序
长轮询
长轮询与下文的服务器发送事件和WebSocket都需要服务器配合。
//服务器
//通过死循环,以资源的修改时间作为循环跳出条件。
//就是上文三次握手的服务器响应阶段,被后端程序的死循环“hold”住。
//当请求的服务器资源的最后一次修改时间==不旧于==客户端请求参数所携带资源的时间(Last-Modified)时,跳出循环,并返回数据。
//客户端
function longPoll(_timeStamp) {
let _timeStamp;
$.get("url-path")
.done(function(data) {
try{
let res = JSON.parse(data);
console.log(res.msg);
}catch(e) {}
})
.always(function() {
setTimeout(function() {
longPoll(_timeStamp || Date.now() / 1000);
}, 10000);
});
}
缺点:服务器资源消耗大。
解决的问题:减少了http请求。
服务器发送事件(Server-sent Event)
该方法有几大特征如下。
- HTML5规范的组成部分。
- 服务器到客户端的单向通信,不需要由客户端发起。
- 以“事件流”的格式产生并推送。
其格式说明如下:- MIME类型:text/event-stream
- event:事件类型
- data:消息内容
- id:用于设置客户端EventSource对象的“last event ID string”内部属性
- retry:指定重新连接的时间
客户端的处理方式: 借助EventSource对象实现。
let eventSource = new EventSource("/path/to/server");
eventSource.onmessage = (e) => {
console.log(e.event, e.data);
};
//或者
eventSource.addEventListener("ping", function(e) {
console.log(e.event, e.data);
});
缺点:所有IE(包括Edge)都不支持该事件,可以通过EventSource Polyfill进行兼容处理(本质上仍然是轮询)。
WebSocket
WebSocket有以下特征:
- HTML5规范的组成部分,现在的版本是RFC6455。
- 实现原理较上文的几种方式不同。
- 基于TCP协议。
- 看到这里,博客和知乎也有说基于HTTP协议的,列出网上的集合以及个人理解的集合图。
知乎上Ovear的详细解答:WebSocket借用HTTP协议来完成了部分握手操作。
//HTTP请求发送时,借助以下字段,通知服务器将协议切换到WebSocket。
Upgrade: websocket
Connection: Upgrade
个人的理解
继续列举它的特征
4. WebSocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。
5. 服务器完成协议升级后,服务端可以主动推送信息给客户端。
6. 需要socket程序实现以及额外的端口。
了解特征五之后,再思考一个问题,WebSocket与本文第二项#服务器发送事件有什么区别,与长轮询呢?
先来看一下Websocket与长轮询的区别。
WebSocket的流程
请求(第一次握手,同时将协议升级到WebSocket):客户端-->Apache/nginx服务器-->后端处理程序
等待1(后端处理程序未通知信息):Apache/nginx服务器-->客户端(保持连接状态,使得服务器一直能够了解客户端的信息?)
等待2(后端处理程序通知信息):后端处理程序-->Apache/nginx服务器-->客户端(通知客户端更新数据)
长轮询的流程
请求(含SYN标记的数据包):客户端-->Apache/nginx服务器-->后端处理程序
响应(含SYN-ACK标记的数据包):后端程序“hold”住请求,待资源更新后才响应-->Apache/nginx服务器-->客户端
确认收到(含ACK标记的数据包):客户端-->Apache/nginx服务器-->后端程序
...重复N次
显而易见的是,WebSocket协议免去了几个重复的流程。有以下几个优点:
- 不需要反复发送和确认http请求,免去服务器对http请求反复解析的工作。
- 资源未更新时,不需要通过后端代码拖延响应的时机。WebSocket把与客户端保持通信的任务交给了服务器,因此减少了本身处理速度就慢的程序的压力。
与服务器发送事件的区别待补充
简单WebSocket服务器实现代码如下。
服务器(Node.js)
var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({ port: 8080 });
wss.on("connection", function(socket) {
socket.on("message", function(ms) {
console.log(msg);
socket.send("Nice to meet you!");
});
});
浏览器作为客户端
//WebSocket为客户端JavaScript的原生对象
var ws = new WebSocket("ws://localhost:8080");
ws.onopen = function(event) {
ws.send("Hello there");
}
ws.onmessage = function(event) {
console.log(event.data);
}
本文是根据参考网址学习所得的笔记和心得。
行为影响状态,状态影响视图