WebSocket 介绍以及配合 STOMP 的使用
由于近期需要使用 WebSocket 的部分功能,然而在工作过程中,发现自己对这部分知识点不是很了解,而且对于后台同学提出的 WebSocket 和 STOMP 的组合,不知如何下手。经过相关资料查证,分享与大家,如有纰漏,希望不吝指出。
本文行文为三个部分,分别讲述:Socket 是什么,WebSocket 是什么,STOMP 是什么,如何结合后两者投入使用。
1. Socket
目前来说,我们经常说的 Socket 的有好几种意思,而且这几种意思还都与通信有关,他们分别是:
- Socket 连接
socket 连接,是端到端的一种连接方式,连接上之后,双方可以互发数据,完成交互;socket 连接的建立也是一个三次握手的过程,经过这个过程之后,双方都可以通过事件监听来获取来自对方的消息(connect, data, close …),也可以主动发送消息给对方(Socket.write)。Socket 连接在不同语言的网络模块均有提供,以上方法都是 node 的 net 模块提供的一些方法和事件,可以用来建立一个完整的 socket 连接。 - Socket 抽象封装层
这一种意思是说,它是作为我们所说的网络分层结构里面的,网络层和应用层之间的一层抽象封装。它的作用,就是将功能强大的网络层的操作做了一个封装,将其复杂的操作,抽象为几个简单的接口供应用层调用,以实现进程在网络中通信。按照网络上流行的说法,TCP/IP(网络层)是功能强大的发动机引擎,Socket 层是汽车,我们只需要动动方向盘,就能调动起强大的引擎为我所用。 - 套接字
这个部分,说的是 Socket 连接建立起来之后,双方维护的一个对象,用来发送和接受数据包。一个 Socket 连接建立,对应的是连接两端对应的一对套接字对象,其维护的信息为:连接使用的协议,本地主机的 IP 地址,本地进程的协议端口,远地主机的 IP 地址,远地进程的协议端口。通过如上信息,即可确定传输的位置和传输的方式。
2. WebSocket
- 是什么
WebSocket 是 H5 规范提出的一种应用层协议(与 HTTP 处于同一层级),是建立在 TCP/IP 协议族之上的一种长连接,可进行全双工通信。 - 为什么需要它
它的提出确实是极其必要的。主要有两方面的考虑:一是,在H5规范的描述下,web应该是一个丰富多彩的世界,能提供应用程序级别的使用体验。既然是应用程序级别体验,自然应该有应用程序级别的网络基础支持,而这种支持就应该包含长连接,实时通信这种级别的支持;二是,使用目前的 HTTP 协议,模拟出两端长连接的效果(轮询,阻塞),消耗太大。 - 实现的过程
WebSocket 连接实现的过程分为两个部分:建立连接的过程,连接之后的 Socket 通信过程。
WebSocket 连接建立的过程,是用到了 HTTP 请求的。在一开始建立连接的过程中,希望建立连接的客户端会向服务端发送一个 HTTP 请求,询问服务器是不是支持 WebSocket,并且告诉服务端,我使用 WebSocket 请求,希望服务端进行相应的响应。
此处为了区分普通的 HTTP 请求,此处上传了其他的头部信息:在客户端校验 Sec-WebSocket-Accept 通过之后,连接即可建立完成。这之后的信息通讯均是WebSocket定义的通过长连接进行的,而且此长连接会复用刚才 HTTP 请求建立的 TCP 长连接。之后的消息发送,消息接受,连接建立,连接关闭等交互,与 Socket 基本类似。1
2
3
4
5
6
7
8
9
10
11
12
13
14// 随机生产的 Base64 字符串,用于安全校验
Sec-WebSocket-Key:n0seXxkGzvDzqsH8ZkDfcg==
// 指定子协议和版本号
Sec-WebSocket-Protocol:v10.stomp, v11.stomp
Sec-WebSocket-Version:13
// 请求服务器升级为WebSocket
Connection:Upgrade
Upgrade:WebSocket
// 服务端的回应
Connection:Upgrade
Sec-WebSocket-Accept:8BiqnztuCvGwd9ine9abKXjtzE0=
Sec-WebSocket-Protocol:v10.stomp
Upgrade:WebSocket 如何使用 node 搭建一个简单的ws服务器
此处的 demo 是,通过 sockjs,建立一个ws服务器,连接两个或者多个客户端,当某一个客户端发送消息给服务器,服务器可以主动将该消息发送给别的客户端。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 服务端主要代码
var http = require('http');
var sockjs = require('sockjs');
// 建立 socket 连接
var sockjs_opts = {sockjs_url: "http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"};
var sockjs_echo = sockjs.createServer(sockjs_opts);
sockjs_echo.on('connection', function(conn) {
conn.on('data', function(message) {
conn.write(message);
});
});
sockjs_echo.installHandlers(server, {prefix:'/echo'});
server.listen(9999, '0.0.0.0');1
2
3
4
5
6
7
8
9
10// 客户端主要代码
var sockjs_url = '/echo';
var sockjs = new SockJS(sockjs_url);
sockjs.onopen = function() {print('[*] open', sockjs.protocol);};
sockjs.onmessage = function(e) {print('[.] message', e.data);};
sockjs.onclose = function() {print('[*] close');};
// 产生交互信息
sockjs.send(‘some message’);点击查看sockjs官方完整demo
3. STOMP
Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许 STOMP 客户端与任意 STOMP 消息代理(Broker)进行交互。 简单来说,就好像HTTP定义了TCP的相关细节一样,STOMP在WebSocket协议之上,告诉信息交互的双方,消息的格式是什么,应该怎样收发的文本协议。具体的定义内容为:
STOMP 是基于 frame(帧)的协议,每个frame都包含了一个 command,一系列的可选 headers 和消息本身的 body,如下:
1 | COMMAND |
上面的空行部分必需,分割 headers 和 body。除了上述的帧内容的定义,协议还对不同的操作定义了不同 COMMAND 的帧。
1 | // 客户端: |
更多命令详解,可参考STOMP协议参考
4. 结合使用
在了解了上诉两个协议之后,我们需要把两方结合起来,让 WebSocket 消息操作变得规范,可控,易于理解。因为 STOMP 协议和 WebSocket 都有已经实现了且可靠的库,在这里我们直接采用。WebSocket 采用 sockjs,STOMP 采用 stompjs。
1 | // 服务端主要代码: |
此处的服务端代码,是直接传入创建的 server,即可使得 server 支持 STOMP 协议。其实在这一步时做了很多工作。其中就有,调用 stompjs 库,将 sockjs 的消息发送用 stomp 进行改写,将 WebSocket 的方法统统用 STOMP 协议的方法进行了包装一遍。这里举消息包装和方法包装的例子说明。
1 | // 当调用 websocket 的 send 方法的时候 |
1 | // 客户端主要代码: |
点击查看完整demo
总结
在各方面了解完 WebSocket 和 STOMP 相关内容之后,其实我们可以发现,STOMP 是个很简单的协议,但是这个简单协议却能有效的规约前后端的交互过程,使交互过程清晰有效。这种用简单高效的抽象,完成通用复杂的工作的方法,其实是很值得我们去借鉴的。另外,在完成这部分内容的探索学习过程中,还顺便学习了一下 npm 包发布的相关内容。感觉学习新东西确实是总能给人带来益处,大家加油!