前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WebSocket 介绍以及配合 STOMP 的使用

WebSocket 介绍以及配合 STOMP 的使用

作者头像
零式的天空
发布2022-03-25 14:51:38
2.9K0
发布2022-03-25 14:51:38
举报
文章被收录于专栏:零域Blog

由于近期需要使用 WebSocket 的部分功能,然而在工作过程中,发现自己对这部分知识点不是很了解,而且对于后台同学提出的 WebSocket 和 STOMP 的组合,不知如何下手。经过相关资料查证,分享与大家,如有纰漏,希望不吝指出。 本文行文为三个部分,分别讲述:Socket 是什么,WebSocket 是什么,STOMP 是什么,如何结合后两者投入使用。

1. Socket

目前来说,我们经常说的 Socket 的有好几种意思,而且这几种意思还都与通信有关,他们分别是:

  1. Socket 连接 socket 连接,是端到端的一种连接方式,连接上之后,双方可以互发数据,完成交互;socket 连接的建立也是一个三次握手的过程,经过这个过程之后,双方都可以通过事件监听来获取来自对方的消息(connect, data, close …),也可以主动发送消息给对方(Socket.write)。Socket 连接在不同语言的网络模块均有提供,以上方法都是 node 的 net 模块提供的一些方法和事件,可以用来建立一个完整的 socket 连接。
  2. Socket 抽象封装层 这一种意思是说,它是作为我们所说的网络分层结构里面的,网络层和应用层之间的一层抽象封装。它的作用,就是将功能强大的网络层的操作做了一个封装,将其复杂的操作,抽象为几个简单的接口供应用层调用,以实现进程在网络中通信。按照网络上流行的说法,TCP/IP(网络层)是功能强大的发动机引擎,Socket 层是汽车,我们只需要动动方向盘,就能调动起强大的引擎为我所用。
  3. 套接字 这个部分,说的是 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 基本类似。

如何使用 node 搭建一个简单的ws服务器 此处的 demo 是,通过 sockjs,建立一个ws服务器,连接两个或者多个客户端,当某一个客户端发送消息给服务器,服务器可以主动将该消息发送给别的客户端。

代码语言:javascript
复制
// 客户端主要代码
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’);

3. STOMP

Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许 STOMP 客户端与任意 STOMP 消息代理(Broker)进行交互。 简单来说,就好像HTTP定义了TCP的相关细节一样,STOMP在WebSocket协议之上,告诉信息交互的双方,消息的格式是什么,应该怎样收发的文本协议。具体的定义内容为: STOMP 是基于 frame(帧)的协议,每个frame都包含了一个 command,一系列的可选 headers 和消息本身的 body,如下:

代码语言:javascript
复制
COMMAND
header1:value1
header2:value2

Body^@

上面的空行部分必需,分割 headers 和 body。除了上述的帧内容的定义,协议还对不同的操作定义了不同 COMMAND 的帧。

代码语言:javascript
复制
// 客户端:
SEND			// 发送消息到服务端,可添加自定义的 header,body 携带内容
SUBSCRIBE		// 用于注册给定目的地send帧,被注册的目的地收到任何消息豆浆通过MESSAGE帧发送过来
UNSUBSCRIBE	// 取消注册监听
BEGIN			// 事务操作开始
COMMIT			// 事务提交
ABORT			// 事务过程中的回滚
ACK			// 确认订阅消息的消费
NACK			// NACK有ACK相反地作用。它地作用是告诉 server client 不想消费这个消息
DISCONNECT		// 断开连接
// 服务端
CONNECT		// 连接建立
RECEIPT		// server 成功处理请求带有 receipt 的 client frame 后的返回
ERROR			// 如果出错的话,server将发送ERRORframe
MESSAGE		// 将订阅的消息发送给client

更多命令详解,可参考STOMP协议参考

4. 结合使用

在了解了上诉两个协议之后,我们需要把两方结合起来,让 WebSocket 消息操作变得规范,可控,易于理解。因为 STOMP 协议和 WebSocket 都有已经实现了且可靠的库,在这里我们直接采用。WebSocket 采用 sockjs,STOMP 采用 stompjs。

代码语言:javascript
复制
// 服务端主要代码:
var http = require("http");
var StompServer = require('stomp-broker-js');

var server = http.createServer();

server.listen(61614);

var stompServer = new StompServer({
  server: server,
  path: '/stomp'
});
// 将监听的客户端放入列表中,方便服务端在接受到消息之后进行转发
stompServer.on('connected', function(sessionId, headers){
  var clientId = headers.clientId;
  if(clientId) {
    clients.push(clientId);
  }
});

stompServer.on('error', function(error) {
  // 将订阅对象减少一个(错误对象)
  clients.splice(clients.length - 1, 1);
  return;
});

// 在每次对应的 roomid 频道收到消息时,转发给所有的订阅者
stompServer.subscribe(config.desination, function(msg, headers) {
  for(var i = 0; i < clients.length; i++) {
    // 如果时debug,打印每次的请求和消息
    if(config.debug) {
      console.log('header: \n' + headers);
      console.log('msg: \n' + msg);
    }
    // 消息转发
    stompServer.send(clients[i], {'content-type': 'application/json'}, JSON.stringify({
      headers: headers,
	  msg: msg
    }));
  }
});

此处的服务端代码,是直接传入创建的 server,即可使得 server 支持 STOMP 协议。其实在这一步时做了很多工作。其中就有,调用 stompjs 库,将 sockjs 的消息发送用 stomp 进行改写,将 WebSocket 的方法统统用 STOMP 协议的方法进行了包装一遍。这里举消息包装和方法包装的例子说明。

代码语言:javascript
复制
// 当调用 websocket 的 send 方法的时候
this.send = function (topic, headers, body) {
  // 将消息内容组装成 stomp 协议的一帧
  var _headers = {};
  if (headers) {
    for (var key in headers) {
      _headers[key] = headers[key];
    }
  }
  var frame = {
    body: body,
    headers: _headers
  };
  var args = {
    dest: topic,
    frame: this.frameParser(frame)
  };
  this.onSend(selfSocket, args);
}

this.onSend = function (socket, args, callback) {
  ...
  this.emit('send', {frame: {headers: frame.headers, body: bodyObj}, dest: args.dest});
  // 将消息发送给订阅方
  this._sendToSubscriptions(socket, args);
  if (callback) {
    callback(true);
  }
};

this._sendToSubscriptions = function (socket, args) {
  ...
  // 确定订阅方,凭借上 command,进行发送
  args.frame.command = "MESSAGE";
  var sock = sub.socket;
  if (sock !== undefined) {
    stomp.StompUtils.sendFrame(sock, args.frame);
  } else {
    this.emit(sub.id, args.frame.body, args.frame.headers);
  }
};
// 发送的时候,还是采用 WebSocket 的发送
function sendFrame(socket, _frame) {
  var frame_str = null;
  if (!_frame.hasOwnProperty('toString')) {
    var frame = new Frame({
      'command': _frame.command,
      'headers': _frame.headers,
      'body': _frame.body
    });
    frame_str = frame.toString();
  } else {
    frame_str = _frame.toString();
  }
  // WebSocket 发送
  socket.send(frame_str);
  return true;
}
代码语言:javascript
复制
// 客户端主要代码:
var url = "ws://localhost:61614/stomp";
var client = Stomp.client(url);

function afterConnect(roomid) {
  btn.addEventListener('click', function () {
    var msg = input.value;
    // 发送信息
    client.send(roomid, {}, msg);
  }, false);
}

function createConnect(roomid, uid) {
  client.connect(headers, function (error) {
    if (error.command == "ERROR") {
      console.error(error.headers.message);
    } else {
      afterConnect(roomid);
      // 订阅自己的客户端id,方便收到服务器发送过来的信息
      client.subscribe(uid, function (msg) {
        var body = msg.body;
        if (msg.headers['content-type'] == 'application/json') {
          body = JSON.parse(msg.body)
        }
      });
    }
  });
}

点击查看完整demo

总结

在各方面了解完 WebSocket 和 STOMP 相关内容之后,其实我们可以发现,STOMP 是个很简单的协议,但是这个简单协议却能有效的规约前后端的交互过程,使交互过程清晰有效。这种用简单高效的抽象,完成通用复杂的工作的方法,其实是很值得我们去借鉴的。另外,在完成这部分内容的探索学习过程中,还顺便学习了一下 npm 包发布的相关内容。感觉学习新东西确实是总能给人带来益处,大家加油!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-01-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Socket
  • 2. WebSocket
  • 3. STOMP
  • 4. 结合使用
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档