WebSocket 与 HTTP/2
个人笔记
WebSocket
WebSocket 是一个双向通信协议,它在握手阶段采用 HTTP/1.1 协议(暂时不支持 HTTP/2)。
握手过程如下:
- 首先客户端向服务端发起一个特殊的 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
- 如果服务端支持该版本的 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 年标准化,主要目的是优化性能。其特性如下:
- 二进制协议:HTTP/2 的消息头使用二进制格式,而非文本格式。并且使用专门设计的 HPack 算法压缩。
- 多路复用(Multiplexing):就是说 HTTP/2 可以重复使用同一个 TCP 连接,并且连接是多路的,多个请求或响应可以同时传输。
- 对比之下,HTTP/1.1 的长连接也能复用 TCP 连接,但是只能串行,不能“多路”。
- 服务器推送:服务端能够直接把资源推送给客户端,当客户端需要这些文件的时候,它已经在客户端了。(该推送对 Web App 是隐藏的,由浏览器处理)
- 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 定义了四种信息:
data
:数据栏event
:自定义数据类型id
:数据 idretry
:最大间隔时间,超时则重新连接
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 的比较
加密与否:
- WebSocket 支持明文通信
ws://
和加密wss://
, - 而 HTTP/2 协议虽然没有规定必须加密,但是主流浏览器都只支持 HTTP/2 over TLS.
- SSE 是使用的 HTTP 协议通信,支持 http/https
- WebSocket 支持明文通信
消息推送:
- 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 也就是慢一点而已。)
参考
- 深入探索 WebSockets 和 HTTP/2
- HTTP/2 特性与抓包分析
- SSE:服务器发送事件,使用长链接进行通讯
- Using server-sent events - MDN
- Can I Use HTTP/2 on Browsers
- Python 3.x how to get http version (using requests library)
- WebSocket 是什么原理?
- 原生模块打造一个简单的 WebSocket 服务器
WebSocket 与 HTTP/2的更多相关文章
- 漫扯:从polling到Websocket
Http被设计成了一个单向的通信的协议,即客户端发起一个request,然后服务器回应一个response.这让服务器很为恼火:我特么才是老大,我居然不能给小弟发消息... 轮询 老大发火了,小弟们自 ...
- 细说WebSocket - Node篇
在上一篇提高到了 web 通信的各种方式,包括 轮询.长连接 以及各种 HTML5 中提到的手段.本文将详细描述 WebSocket协议 在 web通讯 中的实现. 一.WebSocket 协议 1. ...
- java使用websocket,并且获取HttpSession,源码分析
转载请在页首注明作者与出处 http://www.cnblogs.com/zhuxiaojie/p/6238826.html 一:本文使用范围 此文不仅仅局限于spring boot,普通的sprin ...
- WebSocket - ( 一.概述 )
说到 WebSocket,不得不提 HTML5,作为近年来Web技术领域最大的改进与变化,包含CSS3.离线与存储.多媒体.连接性( Connectivity )等一系列领域,而即将介绍的 WebSo ...
- php+websocket搭建简易聊天室实践
1.前言 公司游戏里面有个简单的聊天室,了解了之后才知道是node+websocket做的,想想php也来做个简单的聊天室.于是搜集各种资料看文档.找实例自己也写了个简单的聊天室. http连接分为短 ...
- Demo源码放送:打通B/S与C/S !让HTML5 WebSocket与.NET Socket公用同一个服务端!
随着HTML5 WebSocket技术的日益成熟与普及,我们可以借助WebSocket来更加方便地打通BS与CS -- 因为B/S中的WebSocket可以直接连接到C/S的服务端,并进行双向通信.如 ...
- Cowboy 开源 WebSocket 网络库
Cowboy.WebSockets 是一个托管在 GitHub 上的基于 .NET/C# 实现的开源 WebSocket 网络库,其完整的实现了 RFC 6455 (The WebSocket Pro ...
- 借助Nodejs探究WebSocket
文章导读: 一.概述-what's WebSocket? 二.运行在浏览器中的WebSocket客户端+使用ws模块搭建的简单服务器 三.Node中的WebSocket 四.socket.io 五.扩 ...
- 细说websocket - php篇
下面我画了一个图演示 client 和 server 之间建立 websocket 连接时握手部分,这个部分在 node 中可以十分轻松的完成,因为 node 提供的 net 模块已经对 socket ...
- webSocket and LKDBHelper的使用说明
socketket与lkdbhelper来处理数据 客户需求: 当我们有需要从自己的后台推送消息给我们的用户时,用户需要实时的接收到来自我们的推送消息.前提是没有使用第三方的推送框架,那么这个使用we ...
随机推荐
- Oracle块修改跟踪功能
块修改跟踪功能是指使用二进制文件记录数据库中数据库更改的过程. 其目的是提高增量备份操作的性能,因为RMAN可以使用快修改跟踪文件找到上次执行备份操作后被修改的数据块.这可以节省大量时间,因为如果不这 ...
- iOS Xcode 小技巧,提升理解查询能力,Command + 点击鼠标右键 Jump to Definition等
前言: 介绍下Xcode 小技巧,以及一下快捷键,让你调试程序更加出类拔萃,安排! Command + 点击鼠标右键 Jump to Definition,可能你平时也在用,但是你明白全部的用法吗,试 ...
- LeetCode 中级 - 第k个排列(60)
可以用数学的方法来解, 因为数字都是从1开始的连续自然数, 排列出现的次序可以推 算出来, 对于n=4, k=15 找到k=15排列的过程: 1 + 对2,3,4的全排列 (3!个) 2 + 对1,3 ...
- poj 2186 Popular Cows :求能被有多少点是能被所有点到达的点 tarjan O(E)
/** problem: http://poj.org/problem?id=2186 当出度为0的点(可能是缩点后的点)只有一个时就存在被所有牛崇拜的牛 因为如果存在有两个及以上出度为0的点的话,他 ...
- ABAP术语-Company Code
Company Code 原文:http://www.cnblogs.com/qiangsheng/archive/2008/01/16/1040816.html The smallest organ ...
- PG进程结构和内存结构
本文主要介绍PostgreSQL数据库(后文简称PG)进程结构和内存结构,物理结构将在后续继续整理分享. 上图描述了PG进程结构.内存结构和部分物理结构的内容.图中的内容包含了两个部分: PG ...
- yarn的学习之2-容量调度器和预订系统
本文翻译自 http://hadoop.apache.org/docs/r2.8.0/hadoop-yarn/hadoop-yarn-site/CapacityScheduler.html 和http ...
- 线程池的类型以及执行线程submit()和execute()的区别
就跟题目说的一样,本篇博客,本宝宝主要介绍两个方面的内容,其一:线程池的类型及其应用场景:其二:submit和execute的区别.那么需要再次重申的是,对于概念性的东西,我一般都是从网上挑选截取,再 ...
- 【解决】venv 的名字在 zsh prompt 中不显示
venv 的名字在 zsh prompt 中不显示 ➜ liyongjiandeMBP.lan [/Users/liyongjian/lyj] python3 -m venv lyj_venv ➜ l ...
- vue $set修改对象
在vue开发中,当生成vue实例后,再次给数据赋值时,有时候并不会自动更新到视图上去: eg:<!DOCTYPE html> <html> <head> <m ...