个人笔记

WebSocket

WebSocket 是一个双向通信协议,它在握手阶段采用 HTTP/1.1 协议(暂时不支持 HTTP/2)。

握手过程如下:

  1. 首先客户端向服务端发起一个特殊的 HTTP 请求,其消息头如下:
GET /chat HTTP/1.1  // 请求行
Host: server.example.com
Upgrade: websocket // required
Connection: Upgrade // required
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // required
Origin: http://example.com // 用于防止未认证的跨域脚本使用浏览器 websocket api 与服务端进行通信
Sec-WebSocket-Protocol: chat, superchat // optional, 子协议协商字段
Sec-WebSocket-Version: 13
  1. 如果服务端支持该版本的 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 的握手。

WebSocket 提供两种协议:不加密的 ws:// 和 加密的 wss://. 因为是用 HTTP 握手,它和 HTTP 使用同样的端口:ws 是 80(HTTP),wss 是 443(HTTPS)

在 Python 编程中,可使用 websockets 实现的异步 WebSocket 客户端与服务端。此外 aiohttp 也提供了 WebSocket 支持。

Note:如果你搜索 Flask 的 WebScoket 插件,得到的第一个结果很可能是 Flask-SocketIO。但是 Flask-ScoektIO 使用的是它独有的 SocketIO 协议,并不是标准的 WebSocket。只是它刚好提供与 WebSocket 相同的功能而已。

SocketIO 的优势在于只要 Web 端使用了 SocketIO.js,就能支持该协议。而纯 WS 协议,只有较新的浏览器才支持。对于客户端非 Web 的情况,更好的选择可能是使用 Flask-Sockets。

JS API

// WebSocket API
var socket = new WebSocket('ws://websocket.example.com'); // Show a connected message when the WebSocket is opened.
socket.onopen = function(event) {
console.log('WebSocket is connected.');
}; // Handle messages sent by the server.
socket.onmessage = function(event) {
var message = event.data;
console.log(message);
}; // Handle any error that occurs.
socket.onerror = function(error) {
console.log('WebSocket Error: ' + error);
};

HTTP/2

HTTP/2 于 2015 年标准化,主要目的是优化性能。其特性如下:

  1. 二进制协议:HTTP/2 的消息头使用二进制格式,而非文本格式。并且使用专门设计的 HPack 算法压缩。
  2. 多路复用(Multiplexing):就是说 HTTP/2 可以重复使用同一个 TCP 连接,并且连接是多路的,多个请求或响应可以同时传输。
    • 对比之下,HTTP/1.1 的长连接也能复用 TCP 连接,但是只能串行,不能“多路”。
  3. 服务器推送:服务端能够直接把资源推送给客户端,当客户端需要这些文件的时候,它已经在客户端了。(该推送对 Web App 是隐藏的,由浏览器处理)
  4. HTTP/2 允许取消某个正在传输的数据流(通过发送 RST_STREAM 帧),而不关闭 TCP 连接。
    • 这正是二进制协议的好处之一,可以定义多种功能的数据帧。

它允许服务端将资源推送到客户端缓存,我们访问淘宝等网站时,经常会发现很多请求的请求头部分会提示“provisional headers are shown”,这通常就是直接从缓存加载了资源,因此请求根本没有被发送。观察 Chrome Network 的 Size 列,这种请求的该字段一般都是 from disk cache 或者 from memroy cache.

Chrome 可以通过如下方式查看请求使用的协议:

2019-02-10: 使用 Chrome 查看,目前主流网站基本都已经部分使用了 HTTP/2,知乎、bilibili、GIthub 使用了 wss 协议,也有很多网站使用了 SSE(格式如 data:image/png;base64,<base64 string>

而且很多网站都有使用 HTTP/2 + QUIC,该协议的新名称是 HTTP/3,它是基于 UDP 的 HTTP 协议。

SSE

服务端推送事件,是通过 HTTP 长连接进行信息推送的一个功能。

它首先由浏览器向服务端建立一个 HTTP 长连接,然后服务端不断地通过这个长连接将消息推送给浏览器。JS API 如下:

// create SSE connection
var source = new EventSource('/dates'); // 连接建立时,这些 API 和 WebSocket 的很相似
source.onopen = function(event) {
// handle open event
}; // 收到消息时(它只捕获未命名 event)
source.onmessage = function(event) {
var data = event.data; // 发送过来的实际数据(string)
var origin = event.origin; // 服务器端URL的域名部分,即协议、域名和端口。
var lastEventId = event.lastEventId; // 数据的编号,由服务器端发送。如果没有编号,这个属性为空。
// handle message
}; source.onerror = function(event) {
// handle error event
};

具体的实现

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

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

而 body 部分,SSE 定义了四种信息:

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

body 举例说明:

: 这种格式的消息是注释,会被忽略\n\n
: 通常服务器每隔一段时间就会发送一个注释,防止超时 retry\n\n : 下面这个是一个单行数据\n\n
data: some text\n\n : 下面这个是多行数据,在客户端会重组成一个 data\n\n
data: {\n
data: "foo": "bar",\n
data: "baz", 555\n
data: }\n\n : 这是一个命名 event,只会被事件名与之相同的 listener 捕获\n\n
event: foo\n
data: a foo event\n\n : 未命名事件,会被 onmessage 捕获\n\n
data: an unnamed event\n\n event: bar\n
data: a bar event\n\n : 这个 id 对应 event.lastEventId\n\n
id: msg1\n
data: message\n\n

WebSocket、HTTP/2 与 SSE 的比较

  1. 加密与否:

  2. 消息推送:

    • WebSocket是全双工通道,可以双向通信。而且消息是直接推送给 Web App.
    • SSE 只能单向串行地从服务端将数据推送给 Web App.
    • HTTP/2 虽然也支持 Server Push,但是服务器只能主动将资源推送到客户端缓存!并不允许将数据推送到客户端里跑的 Web App 本身。服务器推送只能由浏览器处理,不会在应用程序代码中弹出服务器数据,这意味着应用程序没有 API 来获取这些事件的通知。
      • 为了接近实时地将数据推送给 Web App, HTTP/2 可以结合 SSE(Server-Sent Event)使用。

WebSocket 在需要接近实时双向通信的领域,很有用武之地。而 HTTP/2 + SSE 适合用于展示实时数据。

另外在客户端非浏览器的情况下,使用不加密的 HTTP/2 也是可能的。

requests 查看 HTTP 协议版本号

可以通过 resp.raw.version 得到响应的 HTTP 版本号:

>>> import requests
>>> resp = requests.get("https://zhihu.com")
>>> resp.raw.version
11

但是 requests 默认使用 HTTP/1.1,并且不支持 HTTP/2.(不过这也不是什么大问题,HTTP/2 只是做了性能优化,用 HTTP/1.1 也就是慢一点而已。)

参考

最新文章

  1. 什么是SQL注入
  2. win7或win2008 R2 被远程登录日志记录 系统日志
  3. aria-expanded,aria-hidden到底做什么用?
  4. poj 2480 Longge&#39;s problem 积性函数
  5. 3D场景优化
  6. EntityFramework使用中的一些Bug
  7. javascript技巧合集
  8. 15个Docker基本命令及用法
  9. java中说明书/开发文档如何编写?
  10. Mac/ios 模拟器 测试模拟慢网速
  11. DirectShow中写push模式的source filter流程 + 源代码(内附详细注释)
  12. EasyUI中datagrid的基本用法
  13. C++11 带来的新特性 (2)—— 统一初始化(Uniform Initialization)
  14. 洛谷P4362 贪吃的九头龙
  15. python抓取bing主页背景图片
  16. kibana添加ES索引403错误解决
  17. CentOS 修改用户密码
  18. Python 日志输出
  19. oracle 创建表空间 、用户 、赋权、建表
  20. django无法加载样式

热门文章

  1. Windows远程桌面连接命令mstsc
  2. Easyui多个下拉框联动效果
  3. NFS网络文件系统详解
  4. bootstrap Table动态绑定数据并自定义字段显示值
  5. HTML基础全荟
  6. jQuery最重要的知识点
  7. Java : java基础(4) 线程
  8. 爬虫之request模块高级
  9. c语言中 *p++ 和 (*p)++ 和 *(p++) 和 *(++p) 和++(*p)和 *(p--)和 *(--p)有什么区别?
  10. asp.net core mvc简介