美文网首页
简易入门:推送技术 Websocket、HTTP2与SSE

简易入门:推送技术 Websocket、HTTP2与SSE

作者: 卡门001 | 来源:发表于2020-09-04 11:18 被阅读0次

在HTML5规范下,最推荐使用ServerSent和WebSocket的方式进行服务器消息的推送,对比这两种方式:

  • ServerSent Events的方式(简称 SSE),可以使服务端的开发依然依用以前的方式,但是其工作方式与Comet类似。
  • 而WebSocket的方式,则对服务端的开发有着较高的要求,但其工作方式是完全的推送。

Websocket

  • WebSocket是HTML5中的协议,支持持久连接
  • WebSocket 是一个双向通信协议,它在握手阶段采用 HTTP/1.1 协议(暂时不支持 HTTP/2)
  • WebSocket 提供两种协议:不加密的 ws:// 和 加密的 wss://
 因为是用 HTTP 握手,它和 HTTP 的端口:ws 是 80(HTTP),wss 是 443(HTTPS)

Websocket API

var ws = new WebSocket(“ws:echo.websocket.org”);
ws.onopen = function(){ws.send(“Test!”); };
ws.onmessage = function(evt){console.log(evt.data);
ws.close();};
ws.onclose = function(evt){console.log(“WebSocketClosed!”);};
ws.onerror = function(evt){console.log(“WebSocketError!”);};

握手过程

1 首先客户端向服务端发起一个特殊的 HTTP 请求,其消息头如下:

GET /chat HTTP/1.1   # 请求行
Origin: http://example.com   #用于防止未认证的跨域脚本使用浏览器 websocket api 与服务端进行通信
Connection: Upgrade  //请求
Host: server.example.com 
Upgrade: websocket  #服务器支持WebSocket协议它将同意
 #16bits 编码得到的 base64 串
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
#可选:子协议协商字段
Sec-WebSocket-Protocol: chat, superchat  
Sec-WebSocket-Version: 13

2 如果服务端支持该版本的 WebSocket,会返回 101 响应,响应标头如下:

HTTP/1.1 101 Switching Protocols  // 状态行
Upgrade: websocket   // required
Connection: Upgrade  // required
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= // required,加密后的 Sec-WebSocket-Key
Sec-WebSocket-Protocol: chat // 表明选择的子协议

握手完成后,接下来的 TCP 数据包就都是 WebSocket 协议的帧了。握手不是 TCP 的握手,而是在 TCP 连接内部,从 HTTP/1.1 upgrade 到 WebSocket 的握手。

工具与实现

  • aiohttp :提供了 WebSocket 支持。
  • python :可使 websockets 实现的异步 WebSocket 客户端与服务端。(代码略)

python 查看协议版本号

>>> import requests
>>> resp = requests.get("https://zhihu.com")      
>>> resp.raw.version
  • node.js中websocket的实现
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {
  // process HTTP request. 
});
server.listen(1337, function() { });
// create the server
wsServer = new WebSocketServer({
  httpServer: server
});
// WebSocket server
wsServer.on('request', function(request) {
  var connection = request.accept(null, request.origin);
  // This is the most important callback for us, we'll handle
  // all messages from users here.
  connection.on('message', function(message) {
      // Process WebSocket message
  });
  connection.on('close', function(connection) {
    // Connection closes
  });
});
  • java语言相关代码(待续)

SSE 与 HTTP2

  • http2的介绍-自行百度

服务端推送事件,是通过 HTTP 长连接进行信息推送的一个功能。
它首先由浏览器向服务端建立一个 HTTP 长连接,然后服务端不断地通过这个长连接将消息推送给浏览器。

严格地说,HTTP 协议无法做到服务器主动推送信息。但是,有一种变通方法,就是服务器向客户端声明,接下来要发送的是流信息(streaming)。也就是说,发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。SSE 就是利用这种机制,使用流信息向浏览器推送信息。它基于 HTTP 协议,目前除了 IE/Edge,其他浏览器都支持

客户端API

SSE 的客户端 API 部署在EventSource对象上。下面的代码可以检测浏览器是否支持 SSE。

if ('EventSource' in window) {
...
}
  1. 浏览器首先生成一个EventSource实例,向服务器发起连接。
// create SSE connection
var url='/dates'
var source = new EventSource(url);

#可以跨域,跨域的方式则代码发如下
# 打开withCredentials属性,表示是否一起发送 Cookie。
var source = new EventSource(url, { withCredentials: true });

EventSource实例的readyState属性

- 0:相当于常量EventSource.CONNECTING,表示连接还未建立,或者断线正在重连。
- 1:相当于常量EventSource.OPEN,表示连接已经建立,可以接受数据。
- 2:相当于常量EventSource.CLOSED,表示连接已断,且不会重连。
  1. 连接建立时,这些 API 和 WebSocket 的很相似。
    连接一旦建立,就会触发open事件,可以在onopen属性定义回调函数。
source.onopen = function(event) {
  // handle open event
};

# 另外一种写法
// 另一种写法
source.addEventListener('open', function (event) {
  // ...
}, false);
  1. 客户端收到服务器发来的数据,就会触发message事件,可以在onmessage属性的回调函数。
source.onmessage = function(event) {
    # 发送过来的实际数据(string)
   var data = event.data;  
   # 服务器端URL的域名部分,即协议、域名和端口。
   var origin = event.origin; 
   # 数据的编号,由服务器端发送。如果没有编号,这个属性为空。
   var lastEventId = event.lastEventId; 
   # handle message
   # TODO: 处理消息
 
};

# onmessage的另一种写法(onerror相同)
source.addEventListener('message', function (event) {
   var data = event.data;
   handle message
}, false);

  1. 如果发生通信错误(比如连接中断),就会触发error事件,可以在onerror属性定义回调函数。
source.onerror = function(event) {
  // handle error event
};

// 另一种写法
source.addEventListener('error', function (event) {
  // handle error event
}, false);
  1. 关闭连接
source.close();
  1. 自定义事件

默认情况下,服务器发来的数据,总是触发浏览器EventSource实例的message事件。开发者还可以自定义 SSE 事件,这种情况下,发送回来的数据不会触发message事件。

source.addEventListener('foo', function (event) {
  var data = event.data;
  // handle message
}, false);

上面代码中,浏览器对 SSE 的foo事件进行监听。如何实现服务器发送foo事件,请看下文。

服务器实现

  1. 数据格式
  • 头部数据

在收到客户端的 SSE 请求(HTTP 协议)时,服务端返回的响应首部如下:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

上面三行之中,第一行的Content-Type必须指定 MIME 类型为event-steam。

  • BODY 部分
    每一次发送的信息,由若干个message组成,每个message之间用\n\n分隔。每个message内部由若干行组成,每一行都是如下格式。
[field]: value\n

上面的field,SSE 定义了四种信息:

  • data:数据栏
  • event:自定义数据类型
  • id :数据 id
  • retry:最大间隔时间,超时则重新连接

此外还可以有冒号开头的行,表示注释。通常,服务器每隔一段时间就会向浏览器发送一个注释,保持连接不中断。

完整的例子

: this is a test stream\n\n
data: some text\n\n
data: another message\n
data: with two lines \n\n

下面是一个发送 JSON 数据的例子。

data: {\n
data: "foo": "bar",\n
data: "baz", 555\n
data: }\n\n

Node 服务器实例(其他待续)

var http = require("http");

http.createServer(function (req, res) {
  var fileName = "." + req.url;

  if (fileName === "./stream") {
    res.writeHead(200, {
      "Content-Type":"text/event-stream",
      "Cache-Control":"no-cache",
      "Connection":"keep-alive",
      "Access-Control-Allow-Origin": '*',
    });
    res.write("retry: 10000\n");
    res.write("event: connecttime\n");
    res.write("data: " + (new Date()) + "\n\n");
    res.write("data: " + (new Date()) + "\n\n");

    interval = setInterval(function () {
      res.write("data: " + (new Date()) + "\n\n");
    }, 1000);

    req.connection.addListener("close", function () {
      clearInterval(interval);
    }, false);
  }
}).listen(8844, "127.0.0.1");

JAVA 这 GRPC over HTTP2 (代码待续)

  • gRPC 完全隐藏了 HTTP/2 本身的 method、headers、path 等语义,这些信息对用户而言完全不可见了。

求统一使用 POST,响应状态统一为 200。只要响应是标准的 gRPC 格式,响应中的 HTTP 状态码将被完全忽略。

  • gRPC 定义了自己的 status 状态码、格式固定的 path、还有它自己的 headers。

SSE与HTTP2 、Wocket比较

虽然HTTP/2提供了很多功能,但它并没有完全满足对现有推送/流技术的需求。

SSE 与 WebSocket 作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。比较HTTP/2和WebSocket,可以看到很多相似之处:


image.png
  • SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议。
  • SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
  • SSE 默认支持断线重连,WebSocket 需要自己实现。
  • SSE 一般只用来传送文本,二进制数据需要编码后传送,WebSocket 默认支持传送二进制数据。
  • SSE 支持自定义发送的消息类型。

如何选择WebSocket和HTTP/2?

WebSockets 会在 HTTP/2 + SSE 的领域中生存下来,主要是因为它是一种已经被很好地应用的技术,并且在非常具体的使用情况下,它比 HTTP/2 更具优势,因为它已经被构建用于具有较少开销(如报头)的双向功能。

假设建立一个大型多人在线游戏,需要来自连接两端的大量消息。在这种情况下,WebSockets 的性能会好很多。

一般情况下,只要需要客户端和服务器之间的真正低延迟,接近实时的连接,就使用 WebSocket ,这可能需要重新考虑如何构建服务器端应用程序,以及将焦点转移到队列事件等技术上。

使用的方案需要显示实时的市场消息,市场数据,聊天应用程序等,依靠 HTTP/2 + SSE 将为你提供高效的双向通信渠道,同时获得留在 HTTP 领域的各种好处:

  • 考虑到与现有 Web 基础设施的兼容性时,WebSocket 通常会变成一个痛苦的源头,因为它将 HTTP 连接升级到完全不同于 HTTP 的协议。
  • 规模和安全性:Web 组件(防火墙,入侵检测,负载均衡)是以 HTTP 为基础构建,维护和配置的,这是大型/关键应用程序在弹性,安全性和可伸缩性方面更偏向的环境。

总体来说,WebSocket 更强大和灵活。因为它是全双工通道,可以双向通信;SSE 是单向通道,只能服务器向浏览器发送,因为流信息本质上就是下载。如果浏览器向服务器发送信息,就变成了另一次 HTTP 请求。

相关文章

网友评论

      本文标题:简易入门:推送技术 Websocket、HTTP2与SSE

      本文链接:https://www.haomeiwen.com/subject/nofzsktx.html