有很多关于 WebSocket 和 HTTP 的博客和讨论,也有很多开发者和网站大力提倡 WebSockets,但我还是不明白为什么。
例如(WebSocket 爱好者的论点):
HTML5 Web Sockets 代表了 Web 通信的下一个发展——全双工、双向通信通道,通过 Web 上的单个套接字运行。 - websocket.org
HTTP 支持流式传输:请求正文流式传输(您在上传大文件时使用它)和响应正文流式传输。
在与 WebSocket 建立连接期间,客户端和服务器每帧交换数据,每帧 2 个字节,而连续轮询时的 HTTP 标头为 8 KB。
为什么这 2 个字节不包括 TCP 和 TCP 协议开销?
GET /about.html HTTP/1.1
Host: example.org
这是约 48 字节的 HTTP 标头。
HTTP 分块编码 - Chunked transfer encoding:
23
This is the data in the first chunk
1A
and this is the second one
3
con
8
sequence
0
因此,每个块的开销并不大。
此外,这两种协议都通过 TCP 工作,因此所有与长连接的 TCP 问题仍然存在。
问题:
为什么 WebSockets 协议更好?为什么实施它而不是更新 HTTP 协议?
1)为什么WebSockets协议更好?
WebSockets 更适合涉及低延迟通信的情况,尤其是客户端到服务器消息的低延迟。对于服务器到客户端的数据,您可以使用长期连接和分块传输获得相当低的延迟。但是,这对客户端到服务器的延迟没有帮助,这需要为每个客户端到服务器的消息建立新的连接。
您的 48 字节 HTTP 握手对于现实世界的 HTTP 浏览器连接是不现实的,因为请求通常会发送几千字节的数据作为请求的一部分(双向),包括许多标头和 cookie 数据。以下是使用 Chrome 的请求/响应示例:
示例请求(2800 字节包括 cookie 数据,490 字节不包括 cookie 数据):
GET / HTTP/1.1
Host: www.cnn.com
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.68 Safari/537.17
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: [[[2428 byte of cookie data]]]
响应示例(355 字节):
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 13 Feb 2013 18:56:27 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: CG=US:TX:Arlington; path=/
Last-Modified: Wed, 13 Feb 2013 18:55:22 GMT
Vary: Accept-Encoding
Cache-Control: max-age=60, private
Expires: Wed, 13 Feb 2013 18:56:54 GMT
Content-Encoding: gzip
HTTP 和 WebSocket 都具有相同大小的初始连接握手,但是对于 WebSocket 连接,初始握手只执行一次,然后小消息只有 6 个字节的开销(2 个用于标头,4 个用于掩码值)。延迟开销不是来自标头的大小,而是来自解析/处理/存储这些标头的逻辑。此外,TCP 连接建立延迟可能是比每个请求的大小或处理时间更大的因素。
2) 为什么要实现它而不是更新 HTTP 协议?
正在努力重新设计 HTTP 协议以实现更好的性能和更低的延迟,例如 SPDY、HTTP 2.0 和 QUIC。这将改善普通 HTTP 请求的情况,但 WebSockets 和/或 WebRTC DataChannel 的客户端到服务器数据传输的延迟很可能仍然低于 HTTP 协议(或者它将用于看起来很像 WebSockets 的模式无论如何)。
更新:
这是一个思考 Web 协议的框架:
TCP:低层、双向、全双工、保证顺序的传输层。不支持浏览器(通过插件/Flash 除外)。
HTTP 1.0:基于 TCP 的请求-响应传输协议。客户端发出一个完整的请求,服务器给出一个完整的响应,然后关闭连接。请求方法(GET、POST、HEAD)对服务器上的资源具有特定的事务意义。
HTTP 1.1:保持 HTTP 1.0 的请求-响应特性,但允许连接对多个完整请求/完整响应(每个请求一个响应)保持打开状态。请求和响应中仍有完整的标头,但连接被重用且未关闭。 HTTP 1.1 还添加了一些额外的请求方法(OPTIONS、PUT、DELETE、TRACE、CONNECT),它们也具有特定的事务含义。然而,正如 HTTP 2.0 草案提案的介绍中所指出的,HTTP 1.1 流水线并未广泛部署,因此这极大地限制了 HTTP 1.1 在解决浏览器和服务器之间的延迟方面的实用性。
长轮询:对 HTTP(1.0 或 1.1)的一种“破解”,其中服务器不会立即响应(或仅使用标头部分响应)客户端请求。在服务器响应之后,客户端立即发送一个新请求(如果通过 HTTP 1.1,则使用相同的连接)。
HTTP 流式传输:允许服务器向单个客户端请求发送多个响应的多种技术(多部分/分块响应)。 W3C 正在将其标准化为使用文本/事件流 MIME 类型的服务器发送事件。浏览器 API(与 WebSocket API 非常相似)称为 EventSource API。
Comet/服务器推送:这是一个涵盖长轮询和 HTTP 流的总称。 Comet 库通常支持多种技术来尝试和最大化跨浏览器和跨服务器支持。
WebSockets:建立在 TCP 上的传输层,使用 HTTP 友好的升级握手。与作为流传输的 TCP 不同,WebSockets 是基于消息的传输:消息在线路上进行分隔,并在交付给应用程序之前完整地重新组装。 WebSocket 连接是双向的、全双工的和长寿命的。在初始握手请求/响应之后,没有事务语义,并且每条消息的开销很小。客户端和服务器可以随时发送消息,并且必须异步处理消息接收。
SPDY:Google 发起的一项提议,使用更有效的有线协议扩展 HTTP,但保留所有 HTTP 语义(请求/响应、cookie、编码)。 SPDY 引入了一种新的帧格式(以长度为前缀的帧),并指定了一种将 HTTP 请求/响应对分层到新的帧层上的方法。可以压缩标头,并且可以在建立连接后发送新的标头。在浏览器和服务器中有 SPDY 的真实实现。
HTTP 2.0:与 SPDY 具有相似的目标:减少 HTTP 延迟和开销,同时保留 HTTP 语义。当前草案源自 SPDY,定义了升级握手和数据帧,与用于握手和帧的 WebSocket 标准非常相似。另一个 HTTP 2.0 草案提案 (httpbis-speed-mobility) 实际上将 WebSockets 用于传输层,并将 SPDY 多路复用和 HTTP 映射添加为 WebSocket 扩展(WebSocket 扩展在握手期间协商)。
WebRTC/CU-WebRTC:允许在浏览器之间进行点对点连接的提议。这可以实现更低的平均和最大延迟通信,因为底层传输是 SDP/数据报而不是 TCP。这允许数据包/消息的无序传递,从而避免了由丢弃的数据包引起的延迟峰值的 TCP 问题,这会延迟所有后续数据包的传递(以保证按顺序传递)。
QUIC:是一种实验性协议,旨在减少与 TCP 相比的 Web 延迟。从表面上看,QUIC 与在 UDP 上实现的 TCP+TLS+SPDY 非常相似。 QUIC 提供相当于 HTTP/2 的多路复用和流量控制,相当于 TLS 的安全性,以及相当于 TCP 的连接语义、可靠性和拥塞控制。由于 TCP 是在操作系统内核和中间盒固件中实现的,因此对 TCP 进行重大更改几乎是不可能的。然而,由于 QUIC 是建立在 UDP 之上的,它没有这样的限制。 QUIC 是针对 HTTP/2 语义设计和优化的。
参考:
HTTP:
维基百科 HTTP 页面
W3C HTTP 相关草案/协议列表
IETF HTTP/1.1 和 HTTP/2.0 草案列表
服务器发送事件:
W3C 服务器发送事件/事件源候选推荐
W3C 服务器发送事件/事件源草案
网络套接字:
IETF RFC 6455 WebSockets 协议
IETF RFC 6455 WebSocket 勘误表
SPDY:
IETF SPDY 草案
HTTP 2.0:
IETF HTTP 2.0 httpbis-http2 草案
IETF HTTP 2.0 httpbis-speed-mobility 草案
IETF httpbis-network-friendly Draft - 一个较旧的 HTTP 2.0 相关提案
WebRTC:
W3C WebRTC API 草案
IETF WebRTC 草案列表
IETF WebRTC 概述草案
IETF WebRTC 数据通道草案
Microsoft CU-WebRTC 提案起始页
快速:
QUIC 铬项目
IETF QUIC 草案
您似乎认为 WebSocket 是 HTTP 的替代品。它不是。这是一个扩展。
WebSockets 的主要用例是在 Web 浏览器中运行并从服务器接收实时数据的 Javascript 应用程序。游戏就是一个很好的例子。
在 WebSockets 之前,JavaScript 应用程序与服务器交互的唯一方法是通过 XmlHttpRequest
。但是这些有一个主要缺点:除非客户端明确请求,否则服务器无法发送数据。
但是新的 WebSocket 功能允许服务器随时发送数据。这允许以低得多的延迟实现基于浏览器的游戏,而无需使用像 AJAX 长轮询或浏览器插件这样的丑陋黑客。
那么为什么不使用带有流式请求和响应的普通 HTTP
在对另一个答案的评论中,您建议只异步流式传输客户端请求和响应正文。
事实上,WebSockets 基本上就是这样。尝试从客户端打开 WebSocket 连接一开始看起来像一个 HTTP 请求,但标头中的一个特殊指令 (Upgrade: websocket
) 告诉服务器以这种异步模式开始通信。 First drafts of the WebSocket protocol 仅此而已,并进行了一些握手以确保服务器真正理解客户端想要异步通信。但后来意识到代理服务器会因此而感到困惑,因为它们习惯于 HTTP 的通常请求/响应模型。发现了针对代理服务器的 potential attack scenario。为了防止这种情况发生,有必要让 WebSocket 流量看起来不像任何正常的 HTTP 流量。这就是在 the final version of the protocol 中引入掩码键的原因。
常规 REST API 使用 HTTP 作为通信的底层协议,它遵循请求和响应范式,这意味着通信涉及客户端从服务器请求一些数据或资源,然后服务器响应该客户端。但是,HTTP 是一个无状态协议,因此每个请求-响应周期最终都必须重复标头和元数据信息。在频繁重复的请求-响应周期的情况下,这会导致额外的延迟。
https://i.stack.imgur.com/qUyFW.png
使用 WebSockets,虽然通信仍然以初始 HTTP 握手开始,但它会进一步升级以遵循 WebSockets 协议(即,如果服务器和客户端都符合该协议,因为并非所有实体都支持 WebSockets 协议)。
现在使用 WebSockets,可以在客户端和服务器之间建立全双工和持久的连接。这意味着,与请求和响应不同,只要应用程序正在运行,连接就会保持打开状态(即它是持久的),并且由于它是全双工的,因此可以进行双向同时通信,即现在服务器能够当新数据(客户端感兴趣的)可用时,启动通信并将一些数据“推送”到客户端。
https://i.stack.imgur.com/ReJux.png
WebSockets 协议是有状态的,允许您实现发布-订阅(或发布/订阅)消息传递模式,这是实时技术中使用的主要概念,您可以在没有服务器推送的情况下以服务器推送的形式获得新的更新客户端必须重复请求(刷新页面)。此类应用的示例包括优步汽车的位置跟踪、推送通知、实时更新股票市场价格、聊天、多人游戏、实时在线协作工具等。
您可以查看有关 Websockets 的深入研究文章,该文章解释了该协议的历史、它是如何形成的、它的用途以及您如何自己实现它。
这是我所做的关于 WebSocket 的演示视频,以及它们与使用常规 REST API 的不同之处:Standardisation and leveraging the exponential rise in data streaming
对于 TL;DR,这里有 2 美分和一个更简单的版本来回答您的问题:
与 HTTP 相比,WebSockets 提供了以下好处: 连接期间的持久状态连接 低延迟:服务器/客户端之间的近实时通信,因为没有按照 HTTP 要求为每个请求重新建立连接的开销。全双工:服务器和客户端都可以同时发送/接收 WebSocket 和 HTTP 协议旨在解决不同的问题,IE WebSocket 旨在改善双向通信,而 HTTP 被设计为无状态,使用请求/响应模型进行分发。除了出于遗留原因(防火墙/代理渗透)共享端口之外,将它们组合成一个协议并没有太多共同点。
为什么 WebSockets 协议更好?
我不认为我们可以像谁更好那样将它们并排比较。这不会是一个公平的比较,仅仅因为他们正在解决两个不同的问题。他们的要求不同。这就像将苹果与橙子进行比较。它们不一样。
HTTP 是一种请求-响应协议。客户端(浏览器)想要一些东西,服务器给它。那是。如果客户端想要的数据很大,服务器可能会发送流数据以避免不必要的缓冲区问题。这里的主要要求或问题是如何从客户端发出请求以及如何响应他们请求的资源(超文本)。这就是 HTTP 大放异彩的地方。
在 HTTP 中,只有客户端请求。服务器只响应。
WebSocket 不是只有客户端才能请求的请求-响应协议。它是一个套接字(非常类似于 TCP 套接字)。意味着一旦连接打开,任何一方都可以发送数据,直到下划线的 TCP 连接关闭。它就像一个普通的插座。与 TCP 套接字的唯一区别是 WebSocket 可以在 Web 上使用。在网络上,我们对普通套接字有很多限制。大多数防火墙会阻止 HTTP 使用的 80 和 433 以外的其他端口。代理和中介也会有问题。因此,为了使协议更容易部署到现有的基础设施 WebSocket 使用 HTTP 握手来升级。这意味着当第一次连接要打开时,客户端发送一个 HTTP 请求告诉服务器说“那不是 HTTP 请求,请升级到 WebSocket 协议”。
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
一旦服务器理解请求并升级到 WebSocket 协议,则不再应用任何 HTTP 协议。
所以我的回答是,谁都不比谁好。它们完全不同。
为什么实施它而不是更新 HTTP 协议?
好吧,我们也可以使用 HTTP 的名称制作所有内容。但是我们可以吗?如果它们是两个不同的东西,我会更喜欢两个不同的名字。 Hickson 和 Michael Carter 也是如此。
其他答案似乎没有涉及这里的一个关键方面,那就是您没有提到需要支持 Web 浏览器作为客户端。上面普通 HTTP 的大多数限制都假设您将使用浏览器/JS 实现。
HTTP协议完全能够进行全双工通信;让客户端执行带有分块编码传输的 POST 并且服务器返回带有分块编码主体的响应是合法的。这会将标头开销删除到仅在初始化时。
因此,如果您要寻找的只是全双工、控制客户端和服务器,并且对 WebSocket 的额外框架/功能不感兴趣,那么我认为 HTTP 是一种更简单的方法,具有更低的延迟/CPU(尽管延迟两者实际上只会在微秒或更短的时间内有所不同)。