微信小程序使用stomp.js实现STOMP传输协议的实时聊天

简介:

uniapp开发的小程序中使用

本来使用websocket,后端同事使用了stomp协议,导致前端也需要对应修改。

如何使用

在static/js中新建stomp.js和websocket.js,然后在需要使用的页面引入监听代码+发送代码即可

代码如下:

位置:项目/pages/static/js/stomp.js

1.stomp.js

javascript 复制代码
// Generated by CoffeeScript 1.7.1

/*
   Stomp Over WebSocket http://www.jmesnil.net/stomp-websocket/doc/ | Apache License V2.0

   Copyright (C) 2010-2013 [Jeff Mesnil](http://jmesnil.net/)
   Copyright (C) 2012 [FuseSource, Inc.](http://fusesource.com)
 */

   (function() {
    var Byte, Client, Frame, Stomp,
      __hasProp = {}.hasOwnProperty,
      __slice = [].slice;
  
    Byte = {
      LF: '\x0A',
      NULL: '\x00'
    };
  
    Frame = (function() {
      var unmarshallSingle;
  
      function Frame(command, headers, body) {
        this.command = command;
        this.headers = headers != null ? headers : {};
        this.body = body != null ? body : '';
      }
  
      Frame.prototype.toString = function() {
        var lines, name, skipContentLength, value, _ref;
        lines = [this.command];
        skipContentLength = this.headers['content-length'] === false ? true : false;
        if (skipContentLength) {
          delete this.headers['content-length'];
        }
        _ref = this.headers;
        for (name in _ref) {
          if (!__hasProp.call(_ref, name)) continue;
          value = _ref[name];
          lines.push("" + name + ":" + value);
        }
        if (this.body && !skipContentLength) {
          lines.push("content-length:" + (Frame.sizeOfUTF8(this.body)));
        }
        lines.push(Byte.LF + this.body);
        return lines.join(Byte.LF);
      };
  
      Frame.sizeOfUTF8 = function(s) {
        if (s) {
          return encodeURI(s).match(/%..|./g).length;
        } else {
          return 0;
        }
      };
  
      unmarshallSingle = function(data) {
        var body, chr, command, divider, headerLines, headers, i, idx, len, line, start, trim, _i, _j, _len, _ref, _ref1;
        divider = data.search(RegExp("" + Byte.LF + Byte.LF));
        headerLines = data.substring(0, divider).split(Byte.LF);
        command = headerLines.shift();
        headers = {};
        trim = function(str) {
          return str.replace(/^\s+|\s+$/g, '');
        };
        _ref = headerLines.reverse();
        for (_i = 0, _len = _ref.length; _i < _len; _i++) {
          line = _ref[_i];
          idx = line.indexOf(':');
          headers[trim(line.substring(0, idx))] = trim(line.substring(idx + 1));
        }
        body = '';
        start = divider + 2;
        if (headers['content-length']) {
          len = parseInt(headers['content-length']);
          body = ('' + data).substring(start, start + len);
        } else {
          chr = null;
          for (i = _j = start, _ref1 = data.length; start <= _ref1 ? _j < _ref1 : _j > _ref1; i = start <= _ref1 ? ++_j : --_j) {
            chr = data.charAt(i);
            if (chr === Byte.NULL) {
              break;
            }
            body += chr;
          }
        }
        return new Frame(command, headers, body);
      };
  
      Frame.unmarshall = function(datas) {
        var frame, frames, last_frame, r;
        frames = datas.split(RegExp("" + Byte.NULL + Byte.LF + "*"));
        r = {
          frames: [],
          partial: ''
        };
        r.frames = (function() {
          var _i, _len, _ref, _results;
          _ref = frames.slice(0, -1);
          _results = [];
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            frame = _ref[_i];
            _results.push(unmarshallSingle(frame));
          }
          return _results;
        })();
        last_frame = frames.slice(-1)[0];
        if (last_frame === Byte.LF || (last_frame.search(RegExp("" + Byte.NULL + Byte.LF + "*$"))) !== -1) {
          r.frames.push(unmarshallSingle(last_frame));
        } else {
          r.partial = last_frame;
        }
        return r;
      };
  
      Frame.marshall = function(command, headers, body) {
        var frame;
        frame = new Frame(command, headers, body);
        return frame.toString() + Byte.NULL;
      };
  
      return Frame;
  
    })();
  
    Client = (function() {
      var now;
  
      function Client(ws) {
        this.ws = ws;
        this.ws.binaryType = "arraybuffer";
        this.counter = 0;
        this.connected = false;
        this.heartbeat = {
          outgoing: 10000,
          incoming: 10000
        };
        this.maxWebSocketFrameSize = 16 * 1024;
        this.subscriptions = {};
        this.partialData = '';
      }
  
      Client.prototype.debug = function(message) {
        var _ref;
        return typeof window !== "undefined" && window !== null ? (_ref = window.console) != null ? _ref.log(message) : void 0 : void 0;
      };
  
      now = function() {
        if (Date.now) {
          return Date.now();
        } else {
          return new Date().valueOf;
        }
      };
  
      Client.prototype._transmit = function(command, headers, body) {
        var out;
        out = Frame.marshall(command, headers, body);
        if (typeof this.debug === "function") {
          this.debug(">>> " + out);
        }
        while (true) {
          if (out.length > this.maxWebSocketFrameSize) {
            this.ws.send(out.substring(0, this.maxWebSocketFrameSize));
            out = out.substring(this.maxWebSocketFrameSize);
            if (typeof this.debug === "function") {
              this.debug("remaining = " + out.length);
            }
          } else {
            return this.ws.send(out);
          }
        }
      };
  
      Client.prototype._setupHeartbeat = function(headers) {
        var serverIncoming, serverOutgoing, ttl, v, _ref, _ref1;
        if ((_ref = headers.version) !== Stomp.VERSIONS.V1_1 && _ref !== Stomp.VERSIONS.V1_2) {
          return;
        }
        _ref1 = (function() {
          var _i, _len, _ref1, _results;
          _ref1 = headers['heart-beat'].split(",");
          _results = [];
          for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
            v = _ref1[_i];
            _results.push(parseInt(v));
          }
          return _results;
        })(), serverOutgoing = _ref1[0], serverIncoming = _ref1[1];
        if (!(this.heartbeat.outgoing === 0 || serverIncoming === 0)) {
          ttl = Math.max(this.heartbeat.outgoing, serverIncoming);
          if (typeof this.debug === "function") {
            this.debug("send PING every " + ttl + "ms");
          }
          this.pinger = Stomp.setInterval(ttl, (function(_this) {
            return function() {
              _this.ws.send(Byte.LF);
              return typeof _this.debug === "function" ? _this.debug(">>> PING") : void 0;
            };
          })(this));
        }
        if (!(this.heartbeat.incoming === 0 || serverOutgoing === 0)) {
          ttl = Math.max(this.heartbeat.incoming, serverOutgoing);
          if (typeof this.debug === "function") {
            this.debug("check PONG every " + ttl + "ms");
          }
          return this.ponger = Stomp.setInterval(ttl, (function(_this) {
            return function() {
              var delta;
              delta = now() - _this.serverActivity;
              if (delta > ttl * 2) {
                if (typeof _this.debug === "function") {
                  _this.debug("did not receive server activity for the last " + delta + "ms");
                }
                return _this.ws.close();
              }
            };
          })(this));
        }
      };
  
      Client.prototype._parseConnect = function() {
        var args, connectCallback, errorCallback, headers;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        headers = {};
        switch (args.length) {
          case 2:
            headers = args[0], connectCallback = args[1];
            break;
          case 3:
            if (args[1] instanceof Function) {
              headers = args[0], connectCallback = args[1], errorCallback = args[2];
            } else {
              headers.login = args[0], headers.passcode = args[1], connectCallback = args[2];
            }
            break;
          case 4:
            headers.login = args[0], headers.passcode = args[1], connectCallback = args[2], errorCallback = args[3];
            break;
          default:
            headers.login = args[0], headers.passcode = args[1], connectCallback = args[2], errorCallback = args[3], headers.host = args[4];
        }
        return [headers, connectCallback, errorCallback];
      };
  
      Client.prototype.connect = function() {
        var args, errorCallback, headers, out;
        args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
        out = this._parseConnect.apply(this, args);
        headers = out[0], this.connectCallback = out[1], errorCallback = out[2];
        if (typeof this.debug === "function") {
          this.debug("Opening Web Socket...");
        }
        this.ws.onmessage = (function(_this) {
          return function(evt) {
            var arr, c, client, data, frame, messageID, onreceive, subscription, unmarshalledData, _i, _len, _ref, _results;
            data = typeof ArrayBuffer !== 'undefined' && evt.data instanceof ArrayBuffer ? (arr = new Uint8Array(evt.data), typeof _this.debug === "function" ? _this.debug("--- got data length: " + arr.length) : void 0, ((function() {
              var _i, _len, _results;
              _results = [];
              for (_i = 0, _len = arr.length; _i < _len; _i++) {
                c = arr[_i];
                _results.push(String.fromCharCode(c));
              }
              return _results;
            })()).join('')) : evt.data;
            _this.serverActivity = now();
            if (data === Byte.LF) {
              if (typeof _this.debug === "function") {
                _this.debug("<<< PONG");
              }
              return;
            }
            if (typeof _this.debug === "function") {
              _this.debug("<<< " + data);
            }
            unmarshalledData = Frame.unmarshall(_this.partialData + data);
            _this.partialData = unmarshalledData.partial;
            _ref = unmarshalledData.frames;
            _results = [];
            for (_i = 0, _len = _ref.length; _i < _len; _i++) {
              frame = _ref[_i];
              switch (frame.command) {
                case "CONNECTED":
                  if (typeof _this.debug === "function") {
                    _this.debug("connected to server " + frame.headers.server);
                  }
                  _this.connected = true;
                  _this._setupHeartbeat(frame.headers);
                  _results.push(typeof _this.connectCallback === "function" ? _this.connectCallback(frame) : void 0);
                  break;
                case "MESSAGE":
                  subscription = frame.headers.subscription;
                  onreceive = _this.subscriptions[subscription] || _this.onreceive;
                  if (onreceive) {
                    client = _this;
                    messageID = frame.headers["message-id"];
                    frame.ack = function(headers) {
                      if (headers == null) {
                        headers = {};
                      }
                      return client.ack(messageID, subscription, headers);
                    };
                    frame.nack = function(headers) {
                      if (headers == null) {
                        headers = {};
                      }
                      return client.nack(messageID, subscription, headers);
                    };
                    _results.push(onreceive(frame));
                  } else {
                    _results.push(typeof _this.debug === "function" ? _this.debug("Unhandled received MESSAGE: " + frame) : void 0);
                  }
                  break;
                case "RECEIPT":
                  _results.push(typeof _this.onreceipt === "function" ? _this.onreceipt(frame) : void 0);
                  break;
                case "ERROR":
                  _results.push(typeof errorCallback === "function" ? errorCallback(frame) : void 0);
                  break;
                default:
                  _results.push(typeof _this.debug === "function" ? _this.debug("Unhandled frame: " + frame) : void 0);
              }
            }
            return _results;
          };
        })(this);
        this.ws.onclose = (function(_this) {
          return function() {
            var msg;
            msg = "Whoops! Lost connection to " + _this.ws.url;
            if (typeof _this.debug === "function") {
              _this.debug(msg);
            }
            _this._cleanUp();
            return typeof errorCallback === "function" ? errorCallback(msg) : void 0;
          };
        })(this);
        return this.ws.onopen = (function(_this) {
          return function() {
            if (typeof _this.debug === "function") {
              _this.debug('Web Socket Opened...');
            }
            headers["accept-version"] = Stomp.VERSIONS.supportedVersions();
            headers["heart-beat"] = [_this.heartbeat.outgoing, _this.heartbeat.incoming].join(',');
            return _this._transmit("CONNECT", headers);
          };
        })(this);
      };
  
      Client.prototype.disconnect = function(disconnectCallback, headers) {
        if (headers == null) {
          headers = {};
        }
        this._transmit("DISCONNECT", headers);
        this.ws.onclose = null;
        this.ws.close();
        this._cleanUp();
        return typeof disconnectCallback === "function" ? disconnectCallback() : void 0;
      };
  
      Client.prototype._cleanUp = function() {
        this.connected = false;
        if (this.pinger) {
          Stomp.clearInterval(this.pinger);
        }
        if (this.ponger) {
          return Stomp.clearInterval(this.ponger);
        }
      };
  
      Client.prototype.send = function(destination, headers, body) {
        if (headers == null) {
          headers = {};
        }
        if (body == null) {
          body = '';
        }
        headers.destination = destination;
        return this._transmit("SEND", headers, body);
      };
  
      Client.prototype.subscribe = function(destination, callback, headers) {
        var client;
        if (headers == null) {
          headers = {};
        }
        if (!headers.id) {
          headers.id = "sub-" + this.counter++;
        }
        headers.destination = destination;
        this.subscriptions[headers.id] = callback;
        this._transmit("SUBSCRIBE", headers);
        client = this;
        return {
          id: headers.id,
          unsubscribe: function() {
            return client.unsubscribe(headers.id);
          }
        };
      };
  
      Client.prototype.unsubscribe = function(id) {
        delete this.subscriptions[id];
        return this._transmit("UNSUBSCRIBE", {
          id: id
        });
      };
  
      Client.prototype.begin = function(transaction) {
        var client, txid;
        txid = transaction || "tx-" + this.counter++;
        this._transmit("BEGIN", {
          transaction: txid
        });
        client = this;
        return {
          id: txid,
          commit: function() {
            return client.commit(txid);
          },
          abort: function() {
            return client.abort(txid);
          }
        };
      };
  
      Client.prototype.commit = function(transaction) {
        return this._transmit("COMMIT", {
          transaction: transaction
        });
      };
  
      Client.prototype.abort = function(transaction) {
        return this._transmit("ABORT", {
          transaction: transaction
        });
      };
  
      Client.prototype.ack = function(messageID, subscription, headers) {
        if (headers == null) {
          headers = {};
        }
        headers["message-id"] = messageID;
        headers.subscription = subscription;
        return this._transmit("ACK", headers);
      };
  
      Client.prototype.nack = function(messageID, subscription, headers) {
        if (headers == null) {
          headers = {};
        }
        headers["message-id"] = messageID;
        headers.subscription = subscription;
        return this._transmit("NACK", headers);
      };
  
      return Client;
  
    })();
  
    Stomp = {
      VERSIONS: {
        V1_0: '1.0',
        V1_1: '1.1',
        V1_2: '1.2',
        supportedVersions: function() {
          return '1.1,1.0';
        }
      },
      client: function(url, protocols) {
        var klass, ws;
        if (protocols == null) {
          protocols = ['v10.stomp', 'v11.stomp'];
        }
        klass = Stomp.WebSocketClass || WebSocket;
        ws = new klass(url, protocols);
        return new Client(ws);
      },
      over: function(ws) {
        return new Client(ws);
      },
      Frame: Frame
    };
  
    if (typeof exports !== "undefined" && exports !== null) {
      exports.Stomp = Stomp;
    }
  
    if (typeof window !== "undefined" && window !== null) {
      Stomp.setInterval = function(interval, f) {
        return window.setInterval(f, interval);
      };
      Stomp.clearInterval = function(id) {
        return window.clearInterval(id);
      };
      window.Stomp = Stomp;
    } else if (!exports) {
      self.Stomp = Stomp;
    }
  
  }).call(this);

位置:项目/pages/static/js/websocket.js

2.websocket.js

javascript 复制代码
const Stomp = require('./stomp.js').Stomp;

let socketOpen = false
let socketMsgQueue = []

export default {
  client: null,
  init(url, header ,connectWS) {
    if (this.client) {
      return Promise.resolve(this.client)
    }

    return new Promise((resolve, reject) => {
      const ws = {
        send: this.sendMessage,
        onopen: null,
        onmessage: null,
        close: this.closeSocket,
      }

      wx.connectSocket({ url, header })

      wx.onSocketOpen(function (res) {
        console.log('WebSocket连接已打开!', res)

        socketOpen = true
        for (let i = 0; i < socketMsgQueue.length; i++) {
          ws.send(socketMsgQueue[i])
        }
        socketMsgQueue = []

        ws.onopen && ws.onopen()
      })

      wx.onSocketMessage(function (res) {
        ws.onmessage && ws.onmessage(res)
      })

      wx.onSocketError(function (res) {
        console.log('WebSocket 错误!', res)
      })

      wx.onSocketClose((res) => {
        this.client.disconnect()
        this.client = null
        socketOpen = false
        console.log('WebSocket 已关闭!', res)
        setTimeout(()=>{
          connectWS()
        },3000)
      })

      Stomp.setInterval = function (interval, f) {
        return setInterval(f, interval)
      }
      Stomp.clearInterval = function (id) {
        return clearInterval(id)
      }

      const client = (this.client = Stomp.over(ws))
      client.connect(header, function () {
        console.log('stomp connected')
        resolve(client)
      })
    })
  },
  disconnect() {
    wx.closeSocket()
  },
  sendMessage(message) {
    if (socketOpen) {
      wx.sendSocketMessage({
        data: message,
      })
    } else {
      socketMsgQueue.push(message)
    }
  },
  closeSocket() {
    console.log('closeSocket')
  },
}

3.监听+发送代码

javascript 复制代码
import WebSocket from "../../static/js/websocket"
const app = getApp();
data: {
	objUid: '1',
	client: null,
	content: '发送的内容'
},
onLoad(options) {
	// stomp协议请求 
	this.initWS()
},
initWS() {
	WebSocket.init(
		`${app.globalData.WSURL}/chat`,
		// 传参
		{
			// login: 'admin',
			// passcode: 'admin',
		},
		// ws断开回调
		() => {
			this.initWS()
		}
	).then((client) => {
		this.setData({
			client: client
		})
		// 订阅
		client.subscribe(
			// 路径
			`/response/${app.globalData.uid}/${this.data.objUid}`,
			// 接收到的数据
			(res) => {
				console.log(res)
			},
			// 消息不会被确认接收,不确认每次连接都会推送
			// { ack: 'client' } 
		)
	})
},
// 直接调用发送即可
send() {
	this.data.client.send(
		// 路径
		`/child/${app.globalData.uid}/${this.data.objUid}`,
		// 自定义参数 http://jmesnil.net/stomp-websocket/doc/
		{},//priority: 9 
		// 发送文本
		JSON.stringify({ 'content': this.data.content })
	);
},
相关推荐
郭wes代码5 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
.生产的驴10 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
汤姆yu15 小时前
基于微信小程序的乡村旅游系统
微信小程序·旅游·乡村旅游
计算机徐师兄15 小时前
基于TP5框架的家具购物小程序的设计与实现【附源码、文档】
小程序·php·家具购物小程序·家具购物微信小程序·家具购物
曲辒净16 小时前
微信小程序实现二维码海报保存分享功能
微信小程序·小程序
朽木成才17 小时前
小程序快速实现大模型聊天机器人
小程序·机器人
peachSoda717 小时前
随手记:小程序使用uni.createVideoContext视频无法触发播放
小程序
何极光17 小时前
uniapp小程序样式穿透
前端·小程序·uni-app
小墨&晓末18 小时前
【PythonGui实战】自动摇号小程序
python·算法·小程序·系统安全
oil欧哟1 天前
🤔认真投入一个月做的小程序,能做成什么样子?有人用吗?
前端·vue.js·微信小程序