网络协议之:WebSocket的消息格式
简介
我们知道WebSocket是建立在TCP协议基础上的一种网络协议,用来进行客户端和服务器端的实时通信。非常的好用。最简单的使用WebSocket的办法就是直接使用浏览器的API和服务器端进行通信。
本文将会深入分析WebSocket的消息交互格式,让大家得以明白,websocket到底是怎么工作的。
WebSocket的握手流程
我们知道WebSocket为了兼容HTTP协议,是在HTTP协议的基础之上进行升级得到的。在客户端和服务器端建立HTTP连接之后,客户端会向服务器端发送一个升级到webSocket的协议,如下所示:
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
注意,这里的HTTP版本必须是1.1以上。HTTP的请求方法必须是GET
通过设置Upgrade和Connection这两个header,表示我们准备升级到webSocket了。
除了这里列的属性之外,其他的HTTP自带的header属性都是可以接受的。
这里还有两个比较特别的header,他们是Sec-WebSocket-Version和Sec-WebSocket-Key。
先看一下Sec-WebSocket-Version, 它表示的是客户端请求的WebSocket的版本号。如果服务器端并不明白客户端发送的请求,则会返回一个400 ("Bad Request"),在这个返回中,服务器端会返回失败的信息。
如果是不懂客户端发送的Sec-WebSocket-Version,服务器端同样会将Sec-WebSocket-Version返回,以告知客户端。
这里要特别关注的一个header字段就是Sec-WebSocket-Key。我们接下来看一下这个字段到底有什么用。
当服务器端收到客户端的请求之后,会返回给客户端一个响应,告诉客户端协议已经从HTTP升级到WebSocket了。
返回的响应可能是这样的:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
这里的Sec-WebSocket-Accept是根据客户端请求中的Sec-WebSocket-Key来生成的。具体而言是将客户端发送的Sec-WebSocket-Key 和 字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 进行连接。然后使用SHA1算法求得其hash值。
最后将hash值进行base64编码即可。
当服务器端返回Sec-WebSocket-Accept之后,客户端可以对其进行校验,已完成整个握手过程。
webSocket的消息格式
之所以要使用webSocket是因为client和server可以随时随地发送消息。这是websocket的神奇所在。那么发送的消息是什么格式的呢?我们来详细看一下。
client和server端进行沟通的消息是以一个个的frame的形式来传输的。frame的格式如下:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
MASK表示的是消息是否是被编码过的,对于从client过来的消息来说,MASK必须是1。如果client发送给server端的消息,MASK不为1,则server需要断开和client的连接。但是server端发送给client端的消息,MASK字段就不需要设置了。
RSV1-3是扩展的字段,可以忽略。
opcode表示怎么去解释payload字段。payload就是实际要传递的消息。0x0表示继续,0x1表示文本,0x2表示二进制,其他的表示控制字段。
FIN表示是否是消息的最后一个frame。如果是0,表示该消息还有更多的frame。如果是1表示,该frame是消息的最后一部分了,可以对消息进行处理了。
为什么需要Payload len字段呢?因为我们需要知道什么时候停止接收消息。所以需要一个表示payload的字段来对消息进行具体的处理。
怎么解析Payload呢?这个就比较复杂。
- 首先读取9-15 bits,将其解析为无符号整数。如果其小于125,那么这个就是payload的长度,结束。如果是126,那么就去到第二步。如果是127,那么就去到第三步。
- 读取下一个16 bits,然后将其解析为无符号整数,结束。
- 读取下一个64 bits。将其解析为符号整数。结束。
如果设置了Mask,那么读取下4个字节,也就是32bits。这个是masking key。当数据读取完毕之后,我们就获取到了编码过后的payload:ENCODED,和MASK key。要解码的话,其逻辑如下:
var DECODED = "";
for (var i = 0; i < ENCODED.length; i++) {
DECODED[i] = ENCODED[i] ^ MASK[i % 4];
FIN可以和opcode一起配合使用,用来发送长消息。
FIN=1表示,是最后一个消息。 0x1表示是text消息,0x2是0,表示是二净值消息,0x0表示消息还没有结束,所以0x0通常和FIN=0 一起使用。
Extensions和Subprotocols
在客户端和服务器端进行握手的过程中,在标准的websocket协议基础之上,客户端还可以发送Extensions或者Subprotocols。这两个有什么区别呢?
首先这两个都是通过HTTP头来设置的。但是两者还是有很大的不同。Extensions可以对WebSocket进行控制,并且修改payload,而subprotocols只是定义了payload的结构,并不会对其进行修改。
Extensions是可选的,而Subprotocols是必须的。
你可以将Extensions看做是数据压缩,它是在webSocket的基础之上,对数据进行压缩或者优化操作,可以让发送的消息更短。
而Subprotocols 表示的是消息的格式,比如使用soap或者wamp。
子协议是在WebSocket协议基础上发展出来的协议,主要用于具体的场景的处理,它是是在WebSocket协议之上,建立的更加严格的规范。
比如,客户端请求服务器时候,会将对应的协议放在Sec-WebSocket-Protocol头中:
GET /socket HTTP/1.1
...
Sec-WebSocket-Protocol: soap, wamp
服务器端会根据支持的类型,做对应的返回,如:
Sec-WebSocket-Protocol: soap
总结
本文讲解了webSocket消息交互的具体格式,可以看到很多强大功能的协议,都是由最最基本的结构组成的。
本文已收录于 http://www.flydean.com/07-websocket-message/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
网络协议之:WebSocket的消息格式的更多相关文章
- MQTT-SN协议乱翻之消息格式
前言 紧接着上篇初步介绍,本文为第二篇,主要梳理MQTT-SN 1.2协议中定义的消息格式. 通用消息格式 消息头 其它可变部分 2/4字节表示 N字节组成 消息头部 长度 消息类型 1或3个字节 1 ...
- 详解FIX协议的原理、消息格式及配置开发
一.定义 FIX协议是由国际FIX协会组织提供的一个开放式协议,目的是推动国际贸易电子化的进程,在各类参与者之间,包括投资经理.经纪人,买方.卖方建立起实时的电子化通讯协议.FIX协议的目标是把各类证 ...
- 小学生都能读懂的网络协议之:WebSocket
目录 简介 webSocket vs HTTP HTTP upgrade header websocket的优点 webScoket的应用 websocket的握手流程 WebSocket API 总 ...
- 边界安全 - CDN/DMZ/网络协议
CDN 工具 - LuManager CDN DMZ 网络协议 - DNS Win7下搭建DNS服务器 - BIND 根域 顶级域(即相关国家域名管理机构的数据库,如中国的CNNIC) com n ...
- 一文详解 WebSocket 网络协议
WebSocket 协议运行在TCP协议之上,与Http协议同属于应用层网络数据传输协议.WebSocket相比于Http协议最大的特点是:允许服务端主动向客户端推送数据(从而解决Http 1.1协议 ...
- 基础笔记(三):网络协议之Tcp、Http
目录 一.网络协议 二.TCP(Transmission Control Protocol,传输控制协议) TCP头格式 TCP协议中的三次握手和四次挥手 TCP报文抓取工具 三.HTTP(Hyper ...
- HTML5 直播协议之 WebSocket 和 MSE
当前为了满足比较火热的移动 Web 端直播需求, 一系列的 HTML5 直播技术迅速的发展了起来. 常见的可用于 HTML5 的直播技术有 HLS, WebSocket 与 WebRTC. 今天我要向 ...
- Kafka的消息格式
Commit Log Kafka储存消息的文件被它叫做log,按照Kafka文档的说法是: Each partition is an ordered, immutable sequence of me ...
- 网络协议 终章 - GTP 协议:复杂的移动网络
前面都是讲电脑上网的情景,今天我们就来认识下使用最多的移动网络上网场景. 移动网络的发展历程 你一定知道手机上网有 2G.3G.4G 的说法,究竟这都是什么意思呢?有一个通俗的说法就是 ...
随机推荐
- Windows Go 开发环境下载、安装并配置
前言 对于我们Windows用户而言,Go提供两种环境安装方式(源码安装除外): 1.MSI安装(MSI文件是Windows Installer的数据包,它实际上是一个数据库,包含安装一种产品所需要的 ...
- noip35
T1 考场乱搞出锅了... 正解: 把原序列按k往左和往右看成两个序列,求个前缀和,找下一个更新的位置,直接暴跳. Code #include<cstdio> #include<cs ...
- mysql版本:'for the right syntax to use near 'identified by 'password' with grant option'
查询mysql具体版本 SELECT @@VERSION 问题分析:mysql版本8.0.13,在给新用户授权时,发生了变化: 1064 - You have an error in your SQL ...
- C++11 shared_ptr智能指针(超级详细)
在实际的 C++ 开发中,我们经常会遇到诸如程序运行中突然崩溃.程序运行所用内存越来越多最终不得不重启等问题,这些问题往往都是内存资源管理不当造成的.比如: 有些内存资源已经被释放,但指向它的指针并没 ...
- WPF---数据绑定(二)
一.绑定到非UI元素 上篇中,我们绑定的数据源均是派生自UIElement的WPF元素.本篇描述的绑定数据源是一个我们自定义的普通的类型. 注:尽管绑定的数据源可以是任意类型的对象,但Path必须总是 ...
- Spring @Order注解的使用
注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响: 1 ...
- SpringBoot以jar包部署需要注意的thymeleaf页面映射问题
关于themeleaf映射需要注意的: 1.页面映射 所有静态页面映射的时候,mapping后面要以/开头(最好),不以/开头也行 但是return 后面路径不能以/开头:IDE中正常,但是打jar包 ...
- EL表达式学习(二)
1.从特定域中获取值: 2.从请求页面的input标签中,获取值:(同servlet中的getParameter和getParameterValues): 3.获取请求头(同servlet中的getH ...
- C++字符串【string】和【char []】操作全攻略
异想之旅:本人博客完全手敲,绝对非搬运,全网不可能有重复:本人无团队,仅为技术爱好者进行分享,所有内容不牵扯广告.本人所有文章发布平台为CSDN.博客园.简书和开源中国,后期可能会有个人博客,除此之外 ...
- Docker与数据:三种挂载方式
操作系统与存储 操作系统中将存储定义为 Volume(卷) ,这是对物理存储的逻辑抽象,以达到对物理存储提供有弹性的分割方式.另外,将外部存储关联到操作系统的动作定义为 Mount(挂载). Dock ...