WebSocket简介

WebSocket协议是一种在浏览器和服务器之间建立长连接的协议,用于实现双向通信。它通过HTTP升级请求来建立连接,握手过程包括客户端发起请求和服务端响应,涉及Upgrade、Connection、Sec-WebSocket-Key等头字段。握手成功后,连接状态变为OPEN,允许数据传输。WebSocket具有低延迟、高效率的特点,适合实时通信应用,如在线游戏、聊天室等。
摘要由CSDN通过智能技术生成

WebSocket 协议

此文仅作为 RFC6455 的学习笔记。篇幅太长超过了简书的单篇最大长度,故分为两篇,此篇记录 1~4 节,其余见 WebSocket 协议 5~10 节;

1.1 背景知识

由于历史原因,在创建一个具有双向通信机制的 web 应用程序时,需要利用到 HTTP 轮询的方式。围绕轮询产生了 “短轮询” 和 “长轮询”。

短轮询

浏览器赋予了脚本网络通信的编程接口 XMLHttpRequest,以及定时器接口 setTimeout。因此,客户端脚本可以每隔一段时间就主动的向服务器发起请求,询问是否有新的信息产生:

  1. 客户端向服务器发起一个请求,询问 “有新信息了吗”
  2. 服务端接收到客户端的请求,但是此时没有新的信息产生,于是直接回复 “没有”,并关闭链接
  3. 客户端知道了没有新的信息产生,那么就暂时什么都不做
  4. 间隔 5 秒钟之后,再次从步骤 1 开始循环执行

长轮询

使用短轮询的方式有一个缺点,由于客户端并不知道服务器端何时会产生新的消息,因此它只有每隔一段时间不停的向服务器询问 “有新信息了吗”。而长轮询的工作方式可以是这样:

  1. 客户端向服务器发起一个请求,询问 “有新信息了吗”
  2. 服务器接收到客户端的请求,此时并没有新的信息产生,不过服务器保持这个链接,像是告诉客户端 “稍等”。于是直到有了新的信息产生,服务端将新的信息返回给客户端。
  3. 客户端接收到消息之后显示出来,并再次由步骤 1 开始循环执行

可以看到 “长轮询” 相较于 “短轮询” 可以减少大量无用的请求,并且客户端接收到新消息的时机将会有可能提前。

继续改进

我们知道 HTTP 协议在开发的时候,并不是为了双向通信程序准备的,起初的 web 的工作方式只是 “请求-返回” 就够了。

但是由于人们需要提高 web 应用程序的用户体验,以及 web 技术本身的便捷性 - 不需要另外的安装软件,使得浏览器也需要为脚本提供一个双向通信的功能,比如在浏览器中做一个 IM(Instant Message)应用或者游戏。

通过 “长、短轮询” 模拟的双向通信,有几个显而易见的缺点:

  1. 每次的请求,都有大量的重复信息,比如大量重复的 HTTP 头。
  2. 即使 “长轮询” 相较 “短轮询” 而言使得新信息到达客户端的及时性可能会有所提高,但是仍有很大的延迟,因为一条长连接结束之后,服务器端积累的新信息要等到下一次客户端和其建立链接时才能传递出去。
  3. 对于开发人员而言,这种模拟的方式是难于调试的

于是,需要一种可以在 “浏览器-服务器” 模型中,提供简单易用的双向通信机制的技术,而肩负这个任务的,就是 WebSocket

1.2 协议概览

协议分为两部分:“握手” 和 “数据传输”。

客户端发出的握手信息类似:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

服务端回应的握手信息类式:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

客户端的握手请求由 请求行(Request-Line) 开始。客户端的回应由 状态行(Status-Line) 开始。请求行和状态行的生产式见 RFC2616

首行之后的部分,都是没有顺序要求的 HTTP Headers。其中的一些 HTTP头 的意思稍后将会介绍,不过也可包括例子中没有提及的头信息,比如 Cookies 信息,见 RFC6265。HTTP头的格式以及解析方式见 RFC2616

一旦客户端和服务端都发送了它们的握手信息,握手过程就完成了,随后就开始数据传输部分。因为这是一个双向的通信,所以客户端和服务端都可以首先发出信息。

在数据传输时,客户端和服务器都使用 “消息 Message” 的概念去表示一个个数据单元,而消息又由一个个 “帧 frame” 组成。这里的帧并不是对应到具体的网络层上的帧。

一个帧有一个与之相关的类型。属于同一个消息的每个帧都有相同的数据类型。粗略的说,有文本类型(以 UTF-8 编码 RFC3629)和二进制类型(可以表示图片或者其他应用程序所需的类型),控制帧(不是传递具体的应用程序数据,而是表示一个协议级别的指令或者信号)。协议中定义了 6 中帧类型,并且保留了 10 种类型为了以后的使用。

1.3 开始握手

握手部分的设计目的就是兼容现有的基于 HTTP 的服务端组件(web 服务器软件)或者中间件(代理服务器软件)。这样一个端口就可以同时接受普通的 HTTP 请求或则 WebSocket 请求了。为了这个目的,WebSocket 客户端的握手是一个 HTTP 升级版的请求(HTTP Upgrade request):

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

为了遵循协议 RFC2616,握手中的头字段是没有顺序要求的。

跟在 GET 方法后面的 “请求标识符 Request-URI” 是用于区别 WebSocket 链接到的不同终节点。一个 IP 可以对应服务于多个域名,这样一台机器上就可以跑多个站点,然后通过 “请求标识符”,单个站点中又可以含有多个 WebSocket 终节点。

Host 头中的服务器名称可以让客户端标识出哪个站点是其需要访问的,也使得服务器得知哪个站点是客户端需要请求的。

其余的头信息是用于配置 WebSocket 协议的选项。典型的一些选项就是,子协议选项 Sec-WebSocket-Protocol、列出客户端支出的扩展 Sec-WebSocket-Extensions、源标识 Origin 等。Sec-WebSocket-Protocol 子协议选项,是用于标识客户端想和服务端使用哪一种子协议(都是应用层的协议,比如 chat 表示采用 “聊天” 这个应用层协议)。客户端可以在 Sec-WebSocket-Protocol 提供几个供服务端选择的子协议,这样服务端从中选取一个(或者一个都不选),并在返回的握手信息中指明,比如:

Sec-WebSocket-Protocol: chat

Origin可以预防在浏览器中运行的脚本,在未经 WebSocket 服务器允许的情况下,对其发送跨域的请求。浏览器脚本在使用浏览器提供的 WebSocket 接口对一个 WebSocket 服务发起连接请求时,浏览器会在请求的Origin 中标识出发出请求的脚本所属的,然后 WebSocket 在接受到浏览器的连接请求之后,就可以根据其中的源去选择是否接受当前的请求。

比如我们有一个 WebSocket 服务运行在 http://websocket.example.com,然后你打开一个网页 http://another.example.com,在个 another 的页面中,有一段脚本试图向我们的 WebSocket 服务发起链接,那么浏览器在其请求的头中,就会标注请求的源为 http://another.example.com,这样我们就可以在自己的服务中选择接收或者拒绝该请求。

服务端为了告知客户端它已经接收到了客户端的握手请求,服务端需要返回一个握手响应。在服务端的握手响应中,需要包含两部分的信息。第一部分的信息来自于客户端的握手请求中的 Sec-WebSocket-Key 头字段:

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

客户端握手请求中的 Sec-WebSocket-Key 头字段中的内容是采用的 base64 编码 RFC4648 的。服务端并不需要将这个值进行反编码,只需要将客户端传来的这个值首先去除首尾的空白,然后和一段固定的 GUID RFC4122字符串进行连接,固定的 GUID 字符串为 258EAFA5-E914-47DA-95CA-C5AB0DC85B11。连接后的结果使用 SHA-1(160数位)FIPS.180-3 进行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值