WebSocket的原理与基于Spring Boot的实现

WebSocket的原理与基于Spring Boot的实现

chenyn911 2018-10-21 22:23:16  1004  收藏 2
分类专栏: Java protocol 文章标签: websocket stomp sockjs spring java
版权
引言
记得几年前微博刚兴起,很多人都会下载来玩。微博上的内容相较于纸质媒体与电视媒体,从信息渠道上较为多元,也比一般的传统媒体更加实时。
当时刚上大学,有了手机,2G网络也已经普及,但资费很贵,没记错的话,一个月30M流量网络要10块钱,超出部分按1元/M来计算。流量很少,但新鲜事物很多,特别是微博上更多(当时小破单机手游别说多火)。一回宿舍就在电脑前开始刷,当“有一条新消息”出现时就要点。包括工作之后,都觉得这个新消息推送很神奇。
前两年查资料是说用长轮询或周期任务来实现,当时还找了个叫Pushlets的框架做了实验,但自从H5被各大浏览器厂商兼容之后,越来越多人开始用Websocket。那Websocket是什么呢?



所有代码都在github:https://github.com/cyancy911/spring-websocket-demo.git ,相应的实现逻辑与类、方法注释都在代码中,有兴趣的可以clone下来。
WebSocket测试插件为Chrome插件:Simple WebSocket Client,链接这里就不贴了。

如果有什么错误可以在评论或私信反馈。

目录
本篇将分6部分讲解WebSocket,主要涉及原理、关系、使用实现等几个部分。
目录如下:

WebSocket是什么
它与http的区别
兼容性如何
基于Spring WebSocket的简单实现
更高级使用sockjs
基于Spring的stomp实现
1.WebSocket是什么
这里主要摘录百度百科和wiki两部分内容给大家参考。


百度百科
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。

wiki
WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection.
翻译:
WebSocket是一种在单TCP连接下,提供全双工通讯通道的计算机通讯协议

ps:好像百度说的大白话些。。。详细了解可看附录链接[wiki]WebSocket

建立连接过程
整个过程分两个阶段
第一个阶段:HTTP请求,握手阶段,变更协议
第二个阶段:成功转换为WebSocket协议进行通讯传输。


第一阶段:
HTTP请求变更协议为WebSocket(请求报文)-> 服务端处理Sec-WebSocket-Key加上盐值(默认为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11)通过SHA-1散列后得到Sec-WebSocket-Accept -> Sec-WebSocket-Accept 经过BASE64编码后回调(响应报文)-> 客户端验证响应报文完整性与正确性

请求报文:
GET /chat HTTP/1.1 - 必须是GET,大于1.1版本
Host: server.example.com - 必须带着请求域名
Upgrade: websocket - 转换头,必须是websocket
Connection: Upgrade - 连接方式,必须是Upgrade
Origin: http://example.com - 跨域,必填
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== - 必填,随机16字节经过base64编码的头
Sec-WebSocket-Protocol: chat, superchat - 可协商子协议,选填,有点像https对称加密协商时的手法
Sec-WebSocket-Version: 13 - 版本,必填,并且必须是13
Sec-WebSocket-Extensions: x-webkit-deflate-frame - 协商层扩展,可选,b端或者s端一方携带一方不携带都会导致连接失败
1
2
3
4
5
6
7
8
9
10
响应报文,准许:
HTTP/1.1 101 Switching Protocols - 必须是101,否则不能转换
Connection:Upgrade - 必须是Upgrade
Server:beetle websocket server - 服务器类型
Upgrade:WebSocket - 必须是websocket
Date:Mon, 26 Nov 2012 23:42:44 GMT
Access-Control-Allow-Credentials:true - 跨域校验成功
Access-Control-Allow-Headers:content-type - 跨域头
Sec-WebSocket-Accept:FCKgUr8c7OsDsLFeJTWrJw6WO8Q= - GUID,丢失、校验不一致、状态不为101都不能转换
Sec-WebSocket-Protocol: chat - 报文子协议,可选

响应报文,错误:
HTTP/1.1 400 Bad Request - 400参数错误
...
Sec-WebSocket-Version: 13, 8, 7 - 指明服务端支持使用版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
第二阶段:
HTTP协议转换为WebSocket协议 -> 所有报文必须通过帧序列封装(mask a sequence frame 这里的帧格式就不说了) -> 否则响应错误状态码(status code)1002并关闭WebSocket连接 -> 正常就通过控制帧和数据帧(UTF-8编码)交替通讯

下边有几个概念比较重要,碎片化,控制帧,数据帧,基本看后边的解释就能懂。

碎片化(Fragmentation):碎片即在传输时把数据内容分块,类似HTTP里的chunk
主要是避免内存缓冲区占满,以及兼容通道复用

例如传输一个经过分块的数据:
数据=123456,碎片化成 块1=123 块2=456
1、CF opcode=1
2、DF 块1
3、CF opcode=0
4、DF 块2
1
2
3
4
5
6
7
8
9
控制帧(Control Frame):主要为了协商协议状态,比如下一帧是做什么的,
通过opcode(operation code 操作码)控制,有下列值:
= 0 - 持续帧(碎片帧,分块传输) Continuation Frame
= 1 - 文本帧 Text Frame
= 2 - 二进制帧 Binary Frame
= 8 - 关闭连接帧 Connection Close Frame
= 9 - 心跳帧 Ping Frame
= A - 响应心跳帧 Pong Frame
1
2
3
4
5
6
7
8
数据帧(Data Frame):就是你实际传输内容的帧,都携带一个叫负载数据(Payload Data)的东西
主要分两部分两种:
1、应用数据(Application Data):你实际传输的数据,必填
2、额外数据(Extension Data):在应用数据之前,选填
主要为了添加额外功能,比如后边说到的sockjs和STOMP协议,可以在额外数据中添加其他未定义的控制帧
1
2
3
4
5
ps:如果还有继续研究的兴趣,可查看附录:[rfc]The WebSocket Protocol,但内容都是英文的~~~

2.它与http的区别
http历史
讲到区别就要讲到历史。HTTP是超文本传输协议(hyper text transfer protocol)的简写,如果从网络比较普及的协议版本1.0开始算,96年到现在,已经有22年的历史,时隔一年,1997年协议又更新发布了1.1版本,经过较长一段时间后,15年又发布了HTTP/2版本。

http特性
HTTP不管是1.0还是1.1,都只在有请求的情况下做出响应,不管请求多频繁,一个Request对应一个Response,按需分配,前置服务器只能给你限制最大单IP并发请求数。这种效率相对低下的请求方式,比较适合于请求较为集中、交互较少的接口或者静态文件如:html、css、img、json等。共同点是阻塞,请求只能同步。而它们比较重要的不同点是通道复用,即1.1相较于1.0多加了默认响应头维持连接Connection: keep-alive,如果当次请求与上一次请求响应的是同一域名地址,tcp连接在b端(browser)或s端(server)没被关闭,那么浏览器还是会一直用上一次的连接进行请求。

新消息提醒实现手段
如果我们想实现像引言所说的微博/twitter新消息提醒功能,可以根据上述HTTP协议,进行频繁轮训(frequent polling)或长轮询(long polling),二者主要的区别在于,前者是快速返回(即无消息立刻返回状态信息),后者是一直连接等待,具体可附录中的:[知乎]WebSocket 是什么原理?为什么可以实现持久连接?一文,相当形象。

但如果是b端频繁请求或者长时间阻塞连接,假设很长一段时间都没有新消息,那必定是对服务器端资源的巨大浪费,s端将在永恒的tcp握手与挥手和数量逐渐增多的阻塞响应中慢慢逼近503。

所以适用于web的长连接Websocket在H5推广之后很快被大家上线使用,对客户端与服务端需要频繁异步请求响应的场景是一个巨大变革。

3.兼容性如何
基本只要浏览器支持H5,一般都能正常连接使用。服务端限制比较少,只要能用socket建立连接,遵循协议内容,你自己都能写一个。

4.基于Spring Websocket的简单实现
可查看github项目中的websocket-demo模块。

启动应用之后,访问域名:ws://localhost:8080/web-socket 正常响应如下截图:


5. 更高级使用sockjs
与websocket不同的是,sockjs不是一个协议,只是一套js库,它的出现旨在让浏览器更加“兼容”WebSocket协议规范。

sockjs
这里我就不列概念了,具体可看参考中的:[github]sockjs、sockjs官网、Sockjs简单介绍,官网上翻译的内容基本跟最后一个网址的内容一致,不再赘述。

它主要做了一层协调,如果浏览器不兼容WebSocket,就通过兼容的协议,使用其他长轮询或流传输方式。

stomp
STOMP虽说是个协议,但其实更像个规范,内容不会像WebSocket一样复杂,它类似于HTTP协议,传输于TCP之上,使用帧传输,效果跟WebSocket很像,但你可以不用WebSocket,只要能实现规范内容就行。

wiki上的概念如下:
Simple (or Streaming) Text Oriented Message Protocol (STOMP), formerly known as TTMP, is a simple text-based protocol, designed for working with message-oriented middleware (MOM).
翻译:
面向简单(或流)文本的消息协议,之前叫TTMP,是一个简单的基于文本的协议,设计用来与面向消息的中间件协同工作。

STOMP的数据帧报文很像HTTP报文,如下:

报文1:
["MESSAGE - 命令
destination:/topic/ws/xuidkl/action - 传输目的地
content-type:application/json;charset=UTF-8
subscription:sub-1
message-id:3hyzmuf2-26402
content-length:315

{
    "changeType": "create",
    "lastModified": 1538211260000
}

\u0000"] - \u0000 报文结尾

报文2:
["CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000

\u0000"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
它主要做了一些命令处理,命令处理如开启一个事务、开始传送数据、提交回滚等:

CONNECT - 建立连接方式
SEND - 发送消息
SUBSCRIBE - 订阅主题
UNSUBSCRIBE - 取消订阅主题
BEGIN - 开始一个事务
COMMIT - 提交事务
ABORT - 回滚事务
ACK - 确认成功消费
NACK - 消费失败
DISCONNECT - 断开连接
1
2
3
4
5
6
7
8
9
10
6. 基于Spring的stomp实现
还是跟上边的4.基于Spring Websocket的简单实现一样,代码都在github仓库,有兴趣的可以上去看。这里列下源码结构和调用逻辑。

代码结构

java:
- config/ - WebSocketConfig - websocket、sockjs、stomp配置
- message/ - TestMessageController - 消息接收接口,订阅接口
- SockjsApplication - 启动类

html:
stomp.html
1
2
3
4
5
6
7
调用逻辑

SockjsApplication启动 
    -> 扫描WebSocketConfig
    -> 生效STOMP注解@EnableWebSocketMessageBroker,创建经纪人Broker
    -> 配置握手地址:/ws,接收消息接口前缀:/app,订阅接口前缀:/topic,用户Session绑定接口前缀:/user
    -> 扫描TestMessageController
    -> 注册接收消息接口:@MessageMapping,注册订阅返回信息接口:@SubscribeMapping
    -> 界面stomp.html调用
1
2
3
4
5
6
7
附录-参考
http基础协议:https://www.cnblogs.com/wangning528/p/6388464.html
http发展史:https://blog.csdn.net/liujianfei526/article/details/53289350
[wiki]WebSocket:https://en.wikipedia.org/wiki/WebSocket
[rfc]The WebSocket Protocol:https://datatracker.ietf.org/doc/rfc6455/?include_text=1
[知乎]WebSocket 是什么原理?为什么可以实现持久连接?:https://www.zhihu.com/question/20215561/answer/40316953
Java for Web学习笔记(四):WebSocket(1)演化历程:https://blog.csdn.net/flowingflying/article/details/65938846
spring WebSocket详解:https://www.cnblogs.com/nosqlcoco/p/5860730.html
[github]sockjs:https://github.com/sockjs
sockjs官网:https://github.com/sockjs/sockjs-client (http://sockjs.org 会自动跳转到这里)
Sockjs简单介绍:https://blog.csdn.net/john_62/article/details/78208177
[wiki]stomp:https://en.wikipedia.org/wiki/Streaming_Text_Oriented_Messaging_Protocol
[github]stomp.js:https://github.com/jmesnil/stomp-websocket
springmvc(18)使用WebSocket 和 STOMP 实现消息功能:https://blog.csdn.net/pacosonswjtu/article/details/51914567
————————————————
版权声明:本文为CSDN博主「chenyn911」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013753225/article/details/83004871

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值