宝塔服务器面板,一键全能部署及管理,送你10850元礼包,点我领取

轮询

传统轮询

传统轮询借助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协议的,列出网上的集合以及个人理解的集合图。

轮询、服务器发送事件与WebSocket-风君雪科技博客

知乎上Ovear的详细解答:WebSocket借用HTTP协议来完成了部分握手操作。

//HTTP请求发送时,借助以下字段,通知服务器将协议切换到WebSocket。
Upgrade: websocket
Connection: Upgrade

轮询、服务器发送事件与WebSocket-风君雪科技博客

个人的理解

继续列举它的特征
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);
}

本文是根据参考网址学习所得的笔记和心得。