我正在学习HTTP/2协议。它是一种带有小消息帧的二进制协议。它允许在单个TCP连接上进行流多路复用。从概念上看,它与WebSockets非常相似。

有没有计划淘汰websockets,代之以某种无头HTTP/2请求和服务器发起的推送消息?或者WebSockets将补充HTTP/2?


当前回答

在读完RFC 7540之后,HTTP/2在所有用例中都使用了过时的websocket,除了将二进制数据从服务器推送到JS webclient。HTTP/2完全支持二进制bidi流(请继续阅读),但浏览器JS没有用于消费二进制数据帧的API,而且AFAIK也没有这样的API计划。

对于bidi流的其他应用,HTTP/2和websockets一样好,甚至更好,因为(1)规范为你做了更多的工作,(2)在许多情况下,它允许较少的TCP连接被打开到一个原点。

PUSH_PROMISE(通俗地称为服务器推送)不是这里的问题。这只是一个性能优化。

Websockets在浏览器中的主要用途是支持双向数据流。所以,我认为OP的问题是HTTP/2是否能更好地在浏览器中实现双向流,我认为是的,它是。

首先,它是bi-di。请阅读streams部分的介绍:

一个“流”是一个独立的、双向的帧序列 在HTTP/2连接中客户端和服务器之间交换。 流有几个重要的特征: 一个HTTP/2连接可以包含多个并发打开的连接 流,任一端点交错来自多个帧 流。 流可以单方面建立和使用,也可以由共享 客户端或服务器。 流可以被任意一个端点关闭。

像这样的文章(在另一个答案中有链接)在HTTP/2的这方面是错误的。他们说那不是比迪。看,有一件事是HTTP/2不能发生的:连接打开后,服务器不能启动常规流,只能启动推送流。但是一旦客户端通过发送请求打开了一个流,双方都可以在任何时候通过一个持久套接字发送数据帧- full bidi。

这与websocket没有太大区别:客户端必须在服务器发送数据之前发起一个websocket升级请求。

最大的不同是,与websockets不同,HTTP/2定义了自己的多路复用语义:流如何获得标识符以及帧如何携带它们所在的流的id。HTTP/2还为流的优先级定义了流控制语义。这在bidi的大多数实际应用中非常重要。

(那篇错误的文章还说Websocket标准有多路复用。不,它没有。这并不难找到,只需打开Websocket RFC 6455并按⌘-F,然后输入“multiplex”。读完之后

该协议是可扩展的;未来的版本可能会引入更多的概念,如多路复用。

你会发现2013年Websocket多路复用的扩展草案。但我不知道哪些浏览器支持这个功能。我不会尝试在这个扩展的背后构建我的SPA web应用程序,特别是随着HTTP/2的到来,支持可能永远不会到来)。

多路复用正是你在为bidi打开websocket时通常必须做的事情,比如说,为一个反应性更新的单页应用程序提供动力。我很高兴它在HTTP/2规范中,得到了一次性的解决。

如果你想知道HTTP/2能做什么,看看gRPC就知道了。gRPC是跨HTTP/2实现的。具体看一下gRPC提供的半双工和全双工流选项。(请注意,gRPC目前不能在浏览器中工作,但这实际上是因为浏览器(1)不会向客户端javascript公开HTTP/2框架,(2)通常不支持gRPC规范中使用的预告片。)

websocket在哪里还有一席之地?其中较大的是server->浏览器推送的二进制数据。HTTP/2确实允许server->浏览器推送二进制数据,但它不会在浏览器JS中公开。对于像推送音频和视频帧这样的应用程序,这是使用websockets的一个原因。

编辑时间:2020年1月17日

随着时间的推移,这个答案逐渐上升到顶部(这很好,因为这个答案或多或少是正确的)。然而,偶尔仍有评论说,由于各种原因,这是不正确的,通常与一些关于PUSH_PROMISE或如何实际消费面向消息的服务器->客户端推在一个单页应用程序的困惑。

如果你需要构建一个实时聊天应用程序,比如说,你需要向聊天室中所有打开连接的客户端广播新的聊天消息,你可以(也可能应该)不需要websockets。

You would use Server-Sent Events to push messages down and the Fetch api to send requests up. Server-Sent Events (SSE) is a little-known but well supported API that exposes a message-oriented server-to-client stream. Although it doesn't look like it to the client JavaScript, under the hood your browser (if it supports HTTP/2) will reuse a single TCP connection to multiplex all of those messages. There is no efficiency loss and in fact it's a gain over websockets because all the other requests on your page are also sharing that same TCP connection. Need multiple streams? Open multiple EventSources! They'll be automatically multiplexed for you.

除了比websocket握手更有效的资源和更少的初始延迟之外,服务器发送事件还有一个很好的属性,它们自动返回并在HTTP/1.1上工作。但是当你有一个HTTP/2连接时,它们工作得非常好。

下面是一篇很好的文章,它提供了一个实现响应式更新SPA的真实示例。

其他回答

根据我的理解,HTTP/2不是websocket的替代品,而是旨在标准化SPDY协议。

在HTTP/2中,服务器推送(server-push)在后台使用,以改善客户端从浏览器加载资源的情况。作为一名开发人员,你在开发过程中并不会真正关心它。然而,使用Websocket,开发者可以使用API,它可以使用唯一的全双工连接来消费和推送消息。

这些不是一回事,它们应该是相辅相成的。

我说不(Websockets并没有过时)。

第一个也是最常被忽视的问题是HTTP/2推送是不可执行的,可能会被代理、路由器、其他中介甚至浏览器忽略。

即(来自HTTP2草案):

中介可以接收来自服务器的推送,并选择不将它们转发到客户端。换句话说,如何利用推送的信息取决于中介。同样,中介可能选择向客户端进行额外的推送,而不需要服务器采取任何操作。

因此,HTTP/2 Push不能取代WebSockets。

此外,HTTP/2连接也会在一段时间后关闭。

的确,该标准规定:

HTTP/2连接是持久的。为了获得最佳性能,预期客户端不会关闭连接,直到确定不需要与服务器进行进一步的通信(例如,当用户导航离开特定的网页时)或直到服务器关闭连接。

但是…

鼓励服务器尽可能长时间地保持打开的连接,但允许在必要时终止空闲连接。当任何一个端点选择关闭传输层TCP连接时,终止端点应该首先发送一个超时帧(章节6.8),以便两个端点可以可靠地确定之前发送的帧是否已经处理,并优雅地完成或终止任何必要的剩余任务。

即使相同的连接允许在打开时推送内容,即使HTTP/2解决了HTTP/1.1的“keep-alive”引入的一些性能问题……HTTP/2连接不会无限期地保持打开状态。

网页也不能在关闭后重新启动HTTP/2连接(除非我们又回到了长时间拖拉的状态)。

EDIT(2017,两年后)

HTTP/2的实现表明,多个浏览器选项卡/窗口共享一个HTTP/2连接,这意味着push永远不会知道它属于哪个选项卡/窗口,从而消除了使用push替代Websockets的情况。

编辑(2020)

我不知道为什么人们开始对答案投反对票。如果说有什么不同的话,那就是从最初的答案发布到现在的几年证明了HTTP/2不能取代WebSockets,而且它的设计初衷也不是这样的。

当然,HTTP/2可能被用于隧道WebSocket连接,但这些隧道连接仍然需要WebSocket协议,它们将影响HTTP/2容器的行为方式。

引用InfoQ的一篇文章:

好吧,答案显然是否定的,原因很简单:正如我们上面所看到的,HTTP/2引入了Server Push,它使服务器能够主动地向客户端缓存发送资源。但是,它不允许将数据下推到客户机应用程序本身。服务器推送仅由浏览器处理,不会弹出到应用程序代码中,这意味着应用程序没有API来获取这些事件的通知。

所以HTTP2推送实际上是浏览器和服务器之间的东西,而Websockets实际上公开了可以被客户端(javascript,如果它运行在浏览器上)和应用程序代码(运行在服务器上)使用的api来传输实时数据。

没有HTTP/2并不会使websockets过时,但是通过HTTP/2的SSE提供了一个可行的替代方案。需要注意的是SSE不支持从服务器到客户端的未经请求的事件(HTTP/2也不支持):即客户端必须通过创建指定事件源端点的EventSource实例显式订阅。因此,您可能不得不稍微重新组织客户端如何安排事件的交付——我想不出有什么场景会出现这种技术障碍。

SSE works with HTTP/1.1. But HTTP/2 makes using SSE generally viable and competitive with websockets in terms of efficiency, instead of practically unusable in the case of HTTP/1.1. Firstly, HTTP/2 multiplexes many event source connections (or rather "streams" in HTTP/2 terms) onto a single TCP connection where as in HTTP/1.1 you'd need one connection for each. According to the HTTP/2 spec, millions of streams can be created per connection by default with the recommended (configurable) minimum being 100, where as browsers maybe severly limited in the number of TCP connections they can make. Second reason is efficiency: many streams in HTTP/2 is requires much less overhead than the many connections required in HTTP/1.1.

最后一件事是,如果你想用SSE取代websockets,你就放弃了一些构建在websockets之上的工具/中间件。我特别想的是套接字。io(这是很多人实际使用websockets的方式),但我相信还有很多。

到今天为止,还没有。

HTTP/2, compared to HTTP, allows you to maintain a connection with a server. From there, you can have multiple streams of data at the same time. The intent is that you can push multiple things at the same time even without the client requesting it. For example, when a browser asks for a index.html, the server might want to also push index.css and index.js. The browser didn't ask for it, but the server might provide it without being asked because it can assume you're going to want in a few seconds.

这比HTTP/1获取index.html,解析它,发现它需要index.js和index.css,然后为这些文件构建2个其他请求更快。HTTP/2允许服务器推送客户端甚至没有要求的数据。

在这种情况下,它类似于WebSocket,但在设计上并不是这样。WebSocket应该允许类似于TCP连接或串行连接的双向通信。它是两者相互通信的套接字。此外,主要的区别是,您可以发送任何原始字节的任意数据包,而不是封装在HTTP协议中。头、路径、查询字符串的概念只在握手期间发生,但是WebSocket打开了一个数据流。

The other difference is you get a lot more fine-tuned access to WebSocket in Javascript, whereas with HTTP, it's handled by the browser. All you get with HTTP is whatever you can fit in XHR/fetch(). That also means the browser will get to intercept and modify HTTP headers without you being able to control it (eg: Origin, Cookies, etc). Also, what HTTP/2 is able to push is sent to the browser. That means JS doesn't always (if ever) know things are being pushed. Again, it makes sense for index.css and index.js because the browser will cache it, but not so much for data packets.

一切都在于名字。HTTP代表超文本传输协议。我们围绕着资产转移的概念。WebSocket是关于构建一个套接字连接,其中二进制数据可以双向传递。


我们没有真正讨论的是SSE(服务器发送事件)。将数据推送到应用程序(JS)不是HTTP/2的意图,但它是SSE的目的。SSE在HTTP/2中得到了真正的加强。但当重要的是数据本身,而不是到达的变量端点时,它并不是WebSockets的真正替代品。对于WebSocket中的每个端点,都会创建一个新的数据流,但是对于SSE,它会在已经存在的HTTP/2会话之间共享。


以下是每项目标的总结:

HTTP—用一个资产响应请求 HTTP/2 -用多个资产响应请求 SSE -响应一个单向文本(UTF-8)事件流 创建一个双向二进制数据流