一文探究web实时通信方案并深入websocket原理与应用

栏目: Html5 · 发布时间: 6年前

内容简介:在最近的项目中,有一个灯态数据展示的需求,要求是实时展示各组灯的灯色与倒计时。在技术层面就是延时要控制到非常低。对于实时类信息获取,我们一般会有4种方案:

在最近的项目中,有一个灯态数据展示的需求,要求是实时展示各组灯的灯色与倒计时。在技术层面就是延时要控制到非常低。

对于实时类信息获取,我们一般会有4种方案:

  • 轮询,浏览器的定时器发起http请求
  • 长轮询(Comet),http1.1支持的由浏览器发起的长轮询
  • websocket,浏览器与后端服务器建立websocket连接,双工(双向)通信
  • SSE(Server-Sent Events),基于HTTP的html5新特性,服务器推送,半双工通信模型

ps :http2.0中有一个服务器推送不是实时需求的方案,这个特性是服务端根据客户端的请求,提前返回多个响应,推送额外的资源给客户端。如果一个请求是由你的主页发送的,服务器可能会响应主页内容、logo以及样式表,因为他知道客户端会用到这些东西。这样不但减轻了数据传送冗余步骤,也加快了页面响应的速度,提高了用户体验。

基于 Flash的socket实现逐渐淘汰,不在考虑范围内。

以下文字版本demo是参考知乎用户@Ovear的回答,推荐大家看下原文,顺便看下该问题的其他回答: www.zhihu.com/question/20…

轮询

轮询是指客户端定时向服务器发送ajax请求,服务器接到请求后马上返回响应信息并关闭连接。

这个是基于“分布式、无状态、基于TCP的请求/响应式”的http协议的。

文字demo

客户端:啦啦啦,有没有新信息(Request)
服务端:没有(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:没有。。(Response)
客户端:啦啦啦,有没有新信息(Request)
服务端:你好烦啊,没有啊。。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:好啦好啦,有啦给你。(Response)
客户端:啦啦啦,有没有新消息(Request)
服务端:。。。。。没。。。。没。。。没有(Response) ---- loop
复制代码

代码demo

<script type="text/javascript">
    //前端Ajax持续调用服务端,称为Ajax轮询技术
    var getting = {
        url:'server.php',
        dataType:'json',
        success:function(res) {
            console.log(res);
            $.ajax(getting); //关键在这里,回调函数内再次请求Ajax
        }        
        //当请求时间过长(默认为60秒),就再次调用ajax长轮询
        error:function(res){
            $.ajax($getting);
        }
    };
    $.ajax(getting);
</script>
复制代码

Comet长轮询,一种hack技术

客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。

Comet的实现主要有两种方式,基于Ajax的长轮询(long-polling)方式和基于 Iframe 及 htmlfile 的流(http streaming)方式。

Ajax的长轮询:

一文探究web实时通信方案并深入websocket原理与应用

基于Iframe的流:

在页面中嵌入一个隐藏的iframe,然后让这个iframe的src属性指向我们请求的一个服务端地址,并且为了数据更新,我们将页面上数据更新操作封装为一个js函数,将函数名当做参数传递到这个地址当中。

服务端收到请求后解析地址取出参数(客户端js函数调用名),每当有数据更新的时候,返回对客户端函数的调用,并且将要跟新的数据以js函数的参数填入到返回内容当中,例如返回“ <script type="text/javascript">update("data")</script> ”这样一个字符串,意味着以data为参数调用客户端update函数进行客户端view更新。

一文探究web实时通信方案并深入websocket原理与应用

文字demo

客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request)
服务端:额。。 等待到有消息的时候。。来 给你(Response)
客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop
复制代码

代码demo

<script type="text/javascript">
    //前端Ajax持续调用服务端,称为Ajax轮询技术
    var getting = {
        url:'server.php',
        dataType:'json',
        success:function(res) {
            console.log(res);
            $.ajax(getting); //关键在这里,回调函数内再次请求Ajax
        }        
        //当请求时间过长(默认为60秒),就再次调用ajax长轮询
        error:function(res){
            $.ajax($getting);
        }
    };
    $.ajax(getting);
</script>
复制代码

websocket

文字demo

客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request)
服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched)
客户端:麻烦你有信息的时候推送给我噢。。
服务端:ok,有的时候会告诉你的。
服务端:balabalabalabala
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
客户端:麻烦你有信息的时候推送给我噢。。
服务端:笑死我了哈哈哈哈哈哈哈
复制代码

代码demo

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function (evt) {
    console.log("Connection open ...");
    ws.send("Hello WebSockets!");
};

ws.onmessage = function (evt) {
    console.log("Received Message: " + evt.data);
    ws.close();
};

ws.onclose = function (evt) {
    console.log("Connection closed.");
};
复制代码

SSE(Server-Sent Event)

所谓SSE,就是浏览器向服务器发送一个HTTP请求,然后服务器不断单向地向浏览器推送“信息”(message)。这种信息在格式上很简单、固定,就是“信息”加上前缀“data: ”,然后以“\n\n”结尾。

SSE 是一种仅使用 HTTP 传送异步消息的 HTML5 标准。不同于 WebSocket,SSE 不需要在后端创建服务器套接字。

后端响应需加入头信息:response.headers["Content-Type"] = "text/event-stream"。

支持的事件有:

onopen 当通往服务器的连接被打开
onmessage 当接收到消息
onerror 当发生错误
复制代码

EventSource.close()来关闭连接。

兼容性: developer.mozilla.org/zh-CN/docs/… IE全系不支持。

文字demo

SSE是单向通道, 只能服务端向浏览器发送数据。特别适用于客户端只需接收从服务器传入的更新的应用程序。

客户端:啦啦啦,我要建立SSE
服务端:ok,有的时候会告诉你的。
服务端:来了来了,有消息了
服务端:balabalabalabala
服务端:哈哈哈哈哈啊哈哈哈哈
服务端:笑死我了哈哈哈哈哈哈哈
复制代码

代码demo

if (typeof (EventSource) !== "undefined") {
    var source = new EventSource("server.php");
    source.onopen = function () {
        console.log("Connection to server opened.");
    };
    source.onmessage = function (event) {

        document.getElementById("result").innerHTML += event.data + "<br>";
    };
    source.onerror = function () {
        console.log("EventSource failed.");
    };
} else {
    document.getElementById("result").innerHTML = "抱歉,你的浏览器不支持 server-sent 事件...";
}
复制代码

选择

目前,我们已经积累了较为丰富轮询请求经验。但是,轮询、长轮询已经无法满足这次需求。主要原因是:灯态数据是500ms上报一次,频次非常高,轮询不适合,有请求丢失和异步跳秒的风险。而且,一般而言轮询都有无谓请求、浪费带宽、效率低下的问题。所以需要从SSE、WebSocket方案中选择。SSE、WebSocket优劣比较如下:

SSE WebSocket
通信类型 半双工(单向) 全双工(双向)
浏览器支持 目前在 Microsoft 浏览器中不可用。 可用于所有主要浏览器。
开发工作量 小:只需发送一条包含特定标头的 HTTP 消息。 中等:需要建立并维护 TCP 套接字通信。在服务器端还需要一个监听器套接字。
扩展性 较弱 较强,支持数据的双向通信

为了后期更好的扩展性,选择了websocket的方案。

深入websocket

简单理解

WebSocket 协议在2008年诞生,2011年成为国际标准。所有现代浏览器都已经支持了。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

一文探究web实时通信方案并深入websocket原理与应用

特点:

  1. 建立在 TCP 协议之上,服务器端的实现比较容易。

  2. 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

  3. 数据格式比较轻量,性能开销小,通信高效。

  4. 可以发送文本,也可以发送二进制数据。

  5. 没有同源限制,客户端可以与任意服务器通信。

  6. 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

ws://example.com:80/some/path
复制代码

客户端实现与API简介

包括ie在内的所有主流浏览器都支持websocket。

  • 构造函数 WebSocket(url[, protocols]) 返回一个 WebSocket 对象
  • 属性
    • WebSocket.binaryType 使用二进制的数据类型连接 blob(Blob 对象表示一个不可变、原始数据的类文件对象。)、arrayBuffer
    • WebSocket.bufferedAmount 只读 未发送至服务器的字节数
    • WebSocket.extensions 只读 服务器选择的扩展
    • WebSocket.onclose 用于指定连接关闭后的回调函数
    • WebSocket.onerror 用于指定连接失败后的回调函数
    • WebSocket.onmessage 用于指定当从服务器接受到信息时的回调函数
    • WebSocket.onopen 用于指定连接成功后的回调函数
    • WebSocket.protocol 只读 服务器选择的下属协议
    • WebSocket.readyState 只读 当前的链接状态
    • WebSocket.url 只读 WebSocket 的绝对路径
  • 方法
    • WebSocket.close([code[, reason]]) 关闭当前链接
    • WebSocket.send(data) 向服务器发送数据

浏览器客户端示例代码:

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function (evt) {
    console.log("Connection open ...");
    ws.send("Hello WebSockets!");
};

ws.onmessage = function (evt) {
    console.log("Received Message: " + evt.data);
    ws.close();
};

ws.onclose = function (evt) {
    console.log("Connection closed.");
};
复制代码

服务端的实现

几乎各种后端语言都有对应的实现方法,支持度较好。

常用的 Node 实现有以下三种。

代码略过,直接到以上项目的GitHub中查看即可。

nginx的支持

在配置 HTTP、HTTPS 域名位置加入如下配置:

location /websocket {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
}
复制代码

Nginx 自从 1.3 版本就开始支持 WebSocket 了,并且可以为 WebSocket 应用程序做反向代理和负载均衡。

WebSockets 受到 Nginx 缺省为60秒的 proxy_read_timeout 的影响。这意味着,如果你有一个程序使用了 WebSocket,但又可能超过60秒不发送任何数据的话,那你要么需要增加超时时间,要么实现一个 ping 的消息(心跳报文)以保持联系。使用 ping 的解决方法有额外的好处,可以发现连接是否被意外关闭。

深入理解

websocket到底是什么?

概念:

HTTP是运行在TCP协议传输层上的应用协议,而WebSocket是通过HTTP协议协商如何连接,然后独立运行在TCP协议传输层上的应用协议。

WebSocket仅仅是利用了HTTP协议做连接请求。WebSocket相当于一个简化版的TCP传输子层(实际上WebSocket也是应用层协议)。

WebSocket之所以能持久连接原因是它运行在TCP协议上,TCP协议自身是长连接协议,所以WebSocket当然可以长连接。为什么HTTP不是长连接,原因是早期的HTTP在发起每个请求,响应完成后就会关闭Socket。但是后来加了多路复用KeepAlive协议后HTTP协议已经可以实现长连接了,可以处理长连接事务了。

所以,Websocket是一个持久化的协议。

特别地:

WebSocket 不是 HTML5 的东西。

WebSocket 是一个协议,归属于 IETF。WebSocket API 是一个 Web API,归属于 W3C。两个规范是独立发布的。

广义上的 HTML5 是一个很宽广的概念,是对大量新 API 的总称, 里面包含的是 WebSocket API,并不是 WebSocket。简单的说,可以把 WebSocket 当成 HTTP,WebSocket API 当成 Ajax。

原理及运行机制

wesocket协议流程图:

一文探究web实时通信方案并深入websocket原理与应用
一文探究web实时通信方案并深入websocket原理与应用

Websocket借用HTTP的协议来完成一部分握手。

典型的Websocket的http握手部分:

1.请求部分

GET ws://xxx.xx.xx.xx:8000/v2x-omp/websocket HTTP/1.1
Host: xxx.xx.xx.xx:8000
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://xxx.xx.xx.xx:8000
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: __guid=120070472.2101968800548691200.1551342012289.0847; iot_v2xshow=_QP5elb46q2pqak9IgU_V0scW3xDh9Qm; monitor_count=1
Sec-WebSocket-Key: Uk07fY3CxNYoq2N5Fl9l1A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
复制代码

和一般http协议不同的主要有:

(1)

Upgrade: websocket
Connection: Upgrade
复制代码

这个是Websocket的核心,告诉Apache、Nginx等服务器:这边发起的是Websocket协议,请用相应的后端来处理。

(2)

Sec-WebSocket-Key: Uk07fY3CxNYoq2N5Fl9l1A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Version: 13
复制代码

Sec-WebSocket-Key 是一个Base64 encode的值,这个是浏览器随机生成的,用于验证交互的服务器。

Sec-WebSocket-Version 是告诉服务器所使用的Websocket Draft(协议版本),避免因版本不同出现兼容性问题。

2.响应部分

服务器会响应如下,成功建立Websocket。

HTTP/1.1 101 Switching Protocols
Server: nginx
Date: Tue, 02 Apr 2019 08:11:57 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: khI5KCJzpRnpR8H2sOx+nnGCDAY=
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
复制代码

至此,HTTP已经完成它所有工作了--连接握手成功,接下来就是完全按照Websocket协议进行了。

websocket传输帧协议:

一文探究web实时通信方案并深入websocket原理与应用

以上所述就是小编给大家介绍的《一文探究web实时通信方案并深入websocket原理与应用》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Essential ActionScript 3.0

Essential ActionScript 3.0

Colin Moock / Adobe Dev Library / June 22, 2007 / $34.64

ActionScript 3.0 is a huge upgrade to Flash's programming language. The enhancements to ActionScript's performance, feature set, ease of use, cleanliness, and sophistication are considerable. Essentia......一起来看看 《Essential ActionScript 3.0》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具