什么是WebSokcet?

WebSocket是一种协议,并且是各大主流浏览器作为客户端支持的协议。它的目标就是用来替代基于 XMLHTTPRequest和长轮询的解决方案。应用在时时弹幕,消息推送,棋牌游戏等需要及时通讯的业务场景。

握手

WebSocket连接有两个阶段:握手(handshake)和数据传输(data transfer)。此握手非TCP三次握手,但是目的差不多,就是客户端告诉浏览器我想要使用WebSocket协议进行通讯。客户端需要发送如下请求,它是一个 HTTP Upgrade 请求:

 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

客户端发送握手请求

  1. Uri要满足如下格式:
ws-URI = "ws:" "//" host [ ":" port ] path [ "?" query ]
wss-URI = "wss:" "//" host [ ":" port ] path [ "?" query ]
  1. 在与服务端建立连接时,客户端必须方发送握手请求,请求是一个HTTP的升级协议(Upgrade)请求。并且该请求必须满足

    • 握手请求必须是一个正常的HTTP请求。
    • 请求方法必须为GET,并且HTTP协议最低为1.1
    • 请求头必须包含Host
    • 请求头必须包含Upgrade,并且值为websocket
    • 请求头必须包含Connection,并且值为Upgrade
    • 请求头必须包含Sec-WebSocket-Key,值为经过Base64转换的长度为16字节的一组数据
    • 请求头必须包含Origin,如果客户端是浏览器这个值肯定是有的,如果非浏览器的客户端,这个值可以随意改。
    • 请求头必须包含Sec-WebSocket-Version,并且值为13
    • 请求头可以带一个Sec-WebSocket-Protocol,这个值告诉服务端客户端想用的子协议,多个用逗号分开
    • 请求头可以带一个Sec-WebSocket-Extensions,这个值告诉服务端客户端支持的协议级别的扩展。
    • 请求头可以带一个和权限校验相关的头,例如Cookie,Authentication等

当客户端将握手请求发出去之后,就要等待服务端的响应了。当服务端成功响应之后,客户端还需要做如下校验:

  1. 返回的响应码非101,例如401,500,403,503 等等,客户端连接失败
  2. 返回的响应头部不包含Upgrade或者Upgrade的值不是websocket,客户端连接失败
  3. 返回的响应头部不包含Connection或者Connection的值不是Upgrade,客户端连接失败
  4. 返回的响应头部不包含Sec-WebSocket-Accept或者Sec-WebSocket-Accept的值并不是Base64(SHA1(Sec-WebSocket-Key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11")),客户端连接失败
  5. 返回的响应头部Sec-WebSocket-Extensions中的值并不是客户端发送的Sec-WebSocket-Extensions中的值,客户端连接失败
  6. 返回的响应头部Sec-WebSocket-Protocol中的值并不是客户端发送的Sec-WebSocket-Protocol中的值,客户端连接失败

服务端接收握手请求

如果服务端在处理请求过程中不满足一下任何一点,服务端都会终止处理该请求

  1. 必须是HTTP1.1+的GET请求
  2. 包含Host请求头
  3. 包含Upgrade值为WebSocket的请求头
  4. 包含Connection值为Upgrade的请求头
  5. 包含Sec-WebSocket-Key值为16字节长度的Base64字符串
  6. 包含Sec-WebSocket-Version值为13的请求头
  7. 非必须:Origin
  8. 非必须:Sec-WebSocket-Protocol
  9. 非必须:Sec-WebSocket-Extensions

当服务端确定这是一个正常的握手请求并且愿意处理此请求,那么服务端需要回应一个HTTP响应:

  1. 状态码必须为 101 Switching Protocol
  2. Upgrade:WebSocket
  3. Connection:Upgrade
  4. Sec-WebSocket-Accept,如上文所说,值为:Base64(SHA1(Sec-WebSocket-Key+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
  5. Sec-WebSocket-Protocol,根据客户端传的值
  6. Sec-WebSocket-Extensions,根据客户端传的值

至此,握手结束。连接状态由CONNECTING进入OPEN状态

协议帧

WebSocket的协议帧格式如下:

  • FIN 1bit

    包结束标志,1 代表最后一个消息包,0代表某一段消息包
  • RSV1, RSV2, RSV3: 每个1bit,共3bit

    值为0,除非协议扩展(Extensions)声明了非0的值的含义。如果服务端收到非0的值,并且没有相应的定义,那么服务端将直接终止连接。
  • Opcode 4bit

    x0 后续帧

    x1 文本帧

    x2 二进制帧

    x3-X7 非控制帧预留

    x8 关闭连接

    x9 PING

    xA PONG

    xB-xF 控制帧预留
  • Mask 1 bit 是否掩码。客户端向服务器发送,必须掩码。服务端向客户端发送不需掩码
  • PayLoad Length 7bits,7+16bits,7+64bits,如果值为 0-125,则数据包长度为0-125.如果值为126,则后2个字节为数据包长度:16bit。如果值为127,则后8个字节为数据包长度:64bit。
  • Masking-Key, 0-4bits.是否有值取决于 Mask 标识位是否为1.
  • Extension data X bytes 如果在握手时协商了扩展,会有值,否则为0
  • Application data y bytes 剩余消息包
  • PayLoad data (x+y)bytes 总消息包=Extension data + Application data.如果有掩码,解码公式如下:
body[i] = body[i] ^ body[i % 4]

代码解析

下面我用tio网络通讯框架代码来解释一下上文中的内容,不必纠结具体代码,只要大概理解代码功能即可。



具体协议升级代码如下:



以上就是握手部分Http协议升级过程的代码部分。没有什么难理解的地方,只要对着文档要求去实现即可。不过要注意的是,这里是升级协议的过程,如果有其他业务处理,比如访问权限校验失败等,可以直接返回 HttpStatusCode 401.

协议帧解析:



总结

大致过了一遍RFC-6455文档,发现还是官方文档中解释的更详细的也更清楚一些,但是苦于英语水平不过关,有些部分理解起来比较困难。

参考资料

RFC-6455

RFC-6455 The WebSocket Protocol 浅读的更多相关文章

  1. The WebSocket Protocol

      [Docs] [txt|pdf] [draft-ietf-hybi-t...] [Diff1] [Diff2] [Errata] Updated by: 7936 PROPOSED STANDAR ...

  2. Supporting Multiple Versions of WebSocket Protocol 支持多版本WebSocket协议

    https://tools.ietf.org/html/rfc6455#section-4.4 4.4. Supporting Multiple Versions of WebSocket Proto ...

  3. The WebSocket Protocol 1000

    https://tools.ietf.org/html/rfc6455 https://tools.ietf.org/html/rfc6455 7.4.1. Defined Status Codes ...

  4. 小王子浅读Effective javascript(一)了解javascript版本

    哈哈,各位园友新年快乐!愚安好久没在园子里写东西了,这次决定针对javascript做一个系列,叫做<小王子浅读Effective javascript>,主要是按照David Herma ...

  5. Handlebars模板引擎中的each嵌套及源码浅读

    若显示效果不佳,可移步到愚安的小窝 Handlebars模板引擎作为时下最流行的模板引擎之一,已然在开发中为我们提供了无数便利.作为一款无语义的模板引擎,Handlebars只提供极少的helper函 ...

  6. Spark 源码浅读-SparkSubmit

    Spark 源码浅读-任务提交SparkSubmit main方法 main方法主要用于初始化日志,然后接着调用doSubmit方法. override def main(args: Array[St ...

  7. 浅读tomcat架构设计之tomcat生命周期(2)

    浅读tomcat架构设计和tomcat启动过程(1) https://www.cnblogs.com/piaomiaohongchen/p/14977272.html tomcat通过org.apac ...

  8. 浅读tomcat架构设计之tomcat容器Container(3)

    浅读tomcat架构设计和tomcat启动过程(1) https://www.cnblogs.com/piaomiaohongchen/p/14977272.html 浅读tomcat架构设计之tom ...

  9. learning websocket protocol

    websocket的产生背景: 众所周知,Web应用的通信过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现.这种机制对于信息变化不是特别频繁 ...

随机推荐

  1. docker命令小全 this is my note.

    服务器类型:linux =>centos 7.X以上版本 常用命令使用紫色加粗标明 1.安装yum-util(为配置docker安装时使用阿里镜像做准备):yum install -y yum- ...

  2. Linux操作系统内核编译之NTFS文件系统模块支持案例

    Linux操作系统内核编译之NTFS文件系统模块支持案例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.内核编译概述 单内核体系设计.但充分借鉴了微内核设计体系的优点,为内核引 ...

  3. MGR基本使用(转)

    第一章:MGR介绍 MGR(MySQL Group Replication)是一个MySQL Server插件,可用于创建弹性,高可用MySQL集群方案.有一个内置的组成员服务,在任何给定的时间点,保 ...

  4. Beta冲刺(5/7)——2019.5.26

    所属课程 软件工程1916|W(福州大学) 作业要求 Beta冲刺(5/7)--2019.5.26 团队名称 待就业六人组 1.团队信息 团队名称:待就业六人组 团队描述:同舟共济扬帆起,乘风破浪万里 ...

  5. Mincut 最小割 (BZOJ1797+最小割+tarjan)

    题目链接 传送门 思路 根据题目给定的边跑一边最大流,然后再在残留网络上跑\(tarjan\). 对于每一条边有: 如果它是非满边,那么它一定不是最小割集里面的边: 如果\(c[u[i]] \not= ...

  6. jenkins构建邮件自动发送,测试邮件发送成功,构建项目邮件发送不成功的问题

    提示问题: Connection error sending email,retrying once more in 10 seconds…… Connection error sending ema ...

  7. Normalize.css重置化样式表

    Normalize.css是一种CSS reset的替代方案.它在默认的HTML元素样式上提供了跨浏览器的高度一致性.相比于传统的CSS reset,Normalize.css是一种现代的.为HTML ...

  8. error: ‘Poco::UInt16’ has not been declared

    碰到Poco库和其他第三方库共用的时候,当include-POCO库的头文件的时候,此时也include-其他库,导致这个报错. 原因是在这两个库中都对 UINT16 定义,导致冲突. 可以把这两个库 ...

  9. 项目Beta冲刺(团队)——05.24(2/7)

    项目Beta冲刺(团队)--05.24(2/7) 格式描述 课程名称:软件工程1916|W(福州大学) 作业要求:项目Beta冲刺(团队) 团队名称:为了交项目干杯 作业目标:记录Beta敏捷冲刺第2 ...

  10. 2017icpc beijing-I题-Colored Nodes

    题意 给定一个n个点m条边的无向图,一开始点i的颜色为i,在第i+kn秒开始时,与节点i相邻的节点会被染成i的颜色(k为自然数) 定义D(i,j)第j秒结束时颜色为i的节点个数,求: $F(i)=\l ...