微信小程序、小游戏的火爆,都让WebSocket的应用变得无处不在。针对这个主题,笔者打算作一个系列博客,旨在由浅入深的介绍WebSocket以及在Springboot和JS中如何快速构建和使用WebSocket提供的能力。前端
本系列计划包含以下几篇文章:nginx
第一篇,什么是WebSocket以及它的用途。
第二篇,Spring中如何利用STOMP快速构建WebSocket广播式消息模式
第三篇,Springboot中,如何利用WebSocket和STOMP快速构建点对点的消息模式(1)
第四篇,Springboot中,如何利用WebSocket和STOMP快速构建点对点的消息模式(2)
第五篇,Springboot中,实现网页聊天室之自定义WebSocket消息代理
第六篇,Springboot中,实现更灵活的WebSocketweb
首先由一个典型场景引出WebSocket的需求场景,进而阐述WebSocket协议自己。包括其定义,特色以及握手过程报文的解读。最后,再次从协议维度和实现长链接的方法两个方面,对比了HTTP与WebSocket的异同,让读者对WebSocket有更深的认识和理解。ajax
为了照顾到刚接触前/后端开发的新手,做为系列的开篇文章,本着由浅入深的目的,本文采用了较为详尽的解读方式,老鸟亦欢迎收藏参考。后续篇章也会陆续更新上线,敬请期待。算法
小铭购买了一张机票,在出发前的几个小时,他但愿经过航班动态查询软件,实时的了解航班动态,如是否有延误,取消等信息。小程序
那么这时查询软件与服务器交互以下图:后端
但是小铭说: 我但愿只查询一次航班动态,当航班有更新时,服务器能够主动把最新的航班动态信息推送给我!微信小程序
怎么办?聪明的程序猿想到了以下的办法:浏览器
即程序内部在小铭第一次请求时,记录下这个请求信息和响应信息,每隔固定时间(例如1分钟)请求一次服务器,服务器返回当前最新状态,对比以前收到的信息,若是相比有变动,则通知小铭;缓存
客户端:有没有新动态(Request)
服务端:正常起飞(Response)
客户端:啦啦啦,有没有新动态(Request)
服务端:正常起飞。。(Response)
客户端:有没有新动态(Request)
服务端:你好烦啊,正常起飞。。(Response)
客户端:有没有新动态(Request)
服务端:好啦好啦,有啦给你,延误30分钟。。(Response)
客户端:有没有新动态(Request)
服务端:没有。。。(Response)
即程序内部依然采用轮询方式,不过比上一个方案相比,采起了阻塞方式。(一直打电话,没收到就不挂电话),也就是说,客户端发起链接后,若是服务端没消息,就一直不返回Response给客户端。直到有消息才通知小铭,以后客户端再次创建链接,周而复始。
客户端:有没有新动态,没有的话就等有了才返回给我吧(Request)
服务端:等到有动态的时候再告诉你。(过了一下子)来了,给你,延误30分钟(Response)
客户端:有没有新动态,没有的话就等有了才返回给我吧(Request)
从整个交互的过程来看,这两种都是很是消耗资源的。
因此它们都有可能发生下面这种状况:
客户端:有新动态么?
服务端:问的人太多了,线路正忙,请稍后再试(503 Server Unavailable)
客户端:。。。。好吧,有新动态么?
服务端:问的人太多了,线路正忙,请稍后再试(503 Server Unavailable)
客户端:。。。。服务端你到底行不行啊。。!@#$%$^&
经过上面这个例子,总结一下咱们能够看出,这两种采用HTTP的方式都不是最好的方式,体如今:
那如今想要达到小铭的要求,该怎么办呢?
说了这么半天了,让咱们言归正传。基于上述的需求和矛盾,WebSocket出现了。
让咱们先来看看,使用了WebSocket之后,上面的场景会变成怎样的流程:
客户端:我要开始使用WebSocket协议,须要的服务:chat(查动态),WebSocket协议版本:13(HTTP Request)
服务端:没问题,已升级为WebSocket协议(HTTP Protocols Switched)
客户端:麻烦航班动态有更新的时候推送通知给我。
服务端:没问题。
(……过了10分钟)
服务端:有动态啦,延误30分钟!
(……过了30分钟)
服务端:有动态啦,如今开始登机!
因而可知,
以下图所示:
WebSocket是HTML5提出的一个协议规范(2011年)附上协议连接:
The WebSocket Protocol RFC6455
WebSocket约定了一个通讯的规范,经过一个握手的机制,客户端(如浏览器)和服务器(WebServer)之间能创建一个相似Tcp的链接,从而方便C-S之间的通讯。
用一张图来描述各个协议的关系:
WebSocket的握手使用HTTP来实现,客户端发送带有Upgrade头的HTTP Request消息。服务端根据请求,作Response。
GET wss://www.example.cn/webSocket HTTP/1.1
Host: www.example.cn
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Version: 13
Origin: http://example.cn
Sec-WebSocket-Key: afmbhhBRQuwCLmnWDRWHxw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
复制代码
详细解释一下:
注:若是对压缩扩展协商的细节感兴趣,可参考下面的RFC7692了解更多细节。 Compression Extensions for WebSocket RFC7692
HTTP/1.1 101
Server: nginx/1.12.2
Date: Sat, 11 Aug 2018 13:21:27 GMT
Connection: upgrade
Upgrade: websocket
Sec-WebSocket-Accept: sLMyWetYOwus23qJyUD/fa1hztc=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
复制代码
详细解释一下:
Sec-WebSocket-Accept字段生成步骤:
- 将Sec-WebSocket-Key与协议中已定义的一个GUID “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”进行拼接。
- 将步骤1中生成的字符串进行SHA1编码。
- 将步骤2中生成的字符串进行Base64编码。
客户端经过验证服务端返回的Sec-WebSocket-Accept的值, 来肯定两件事情:
- 服务端是否理解WebSocket协议, 若是服务端不理解,那么它就不会返回正确的Sec-WebSocket-Accept,则创建WebSocket链接失败。
- 服务端返回的Response是对于客户端的这次请求的,而不是以前的缓存。 主要是防止有些缓存服务器返回缓存的Response.
至此,握手过程就完成了,此时的TCP链接不会释放。客户端和服务端能够互相通讯了。
最后,做为总结,让咱们再来回顾一下HTTP1.1与WebSocket的相同与不一样。加深对WebSocket的理解。
即在必定的期限内保持连接,客户端会须要在短期内向服务端请求大量的资源,保持TCP链接不断开。客户端与服务器通讯,必需要有客户端发起而后服务器返回结果。客户端是主动的,服务器是被动的。在一个TCP链接上能够传输多个Request/Response消息对,因此本质上仍是Request/Response消息对,仍然会形成资源的浪费、实时性不强等问题。若是不是持续链接,即短链接,那么每一个资源都要创建一个新的链接,HTTP底层使用的是TCP,那么每次都要使用三次握手创建TCP链接,即每个request对应一个response,将形成极大的资源浪费。
即客户端发送一个超时时间很长的Request,服务器保持住这个链接,在有新数据到达时返回Response
只需创建一次Request/Response消息对,以后都是TCP链接,避免了须要屡次创建Request/Response消息对而产生的冗余头部信息。节省了大量流量和服务器资源。所以被普遍应用于线上WEB游戏和线上聊天室的开发。
下一篇中,笔者将使用JS(前端)和Springboot(后端),详细介绍如何利用Springboot框架,快速构建一个基于STOMP的简单WebSocket通讯系统。敬请关注。
小铭出品,必属精品
欢迎关注xNPE技术论坛,更多原创干货每日推送。