flutter开发实战-长链接WebSocket使用stomp协议stomp_dart_client

flutter开发实战-长链接WebSocket使用stomp协议stomp_dart_client

在app中经常会使用长连接进行消息通信,这里记录一下基于websocket使用stomp协议的使用。
在这里插入图片描述

一、stomp:流文本定向消息协议

1.1 stomp介绍

stomp,Streaming Text Orientated Message Protocol,是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。
它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互,类似于OpenWire(一种二进制协议)。

1.2 协议支持

stomp 1.0
stomp 1.1 (including heart-beating)

1.3 stomp frame(帧)

stomp frame(帧)对象包括command、headers、body

command和headers属性始终会被定义,若头部信息时,headers参数可为{},body也可能为空

二、flutter上使用stomp

2.1 引入库stomp_dart_client

flutter上使用stomp时,需要在pubspec.yaml引入库如下

# stomp协议长链接stomp_dart_client: ^0.4.4stomp: ^0.8.0

2.2 实现websocketmanager封装stomp

// 管理长链接socket, stomp协议

import 'package:stomp_dart_client/stomp.dart';
import 'package:stomp_dart_client/stomp_config.dart';
import 'package:stomp_dart_client/stomp_frame.dart';// 接收到stomp协议的frame的callback
typedef OnFrameCallback = void Function(StompFrame);enum StompState {IDLE,CREATED,CONNECTING,CONNECTED,RECONNECTING,DISCONNECTED,ERROR,
}class WebSocketStompManager {//私有构造函数WebSocketStompManager._internal();//保存单例static WebSocketStompManager _singleton = WebSocketStompManager._internal();//工厂构造函数factory WebSocketStompManager() => _singleton;// 订阅的Subscription// 保存订阅, id: dynamicMap _subscriptions = Map<String, dynamic>();// stomp的headers信息Map<String, String>? _headers = Map<String, String>();// 是否连接StompState _stompState = StompState.IDLE;// 当前连接的UrlString _urlString = '';// StompClient clientStompClient? _client;// 创建连接void createConnect(String urlString, Map<String, String> headers) {_urlString = urlString;_headers = _headers;_client?.deactivate();_client = null;_client = StompClient(config: StompConfig(url: urlString,// connectionTimeout: Duration(seconds: 10),// stompConnectHeaders: {//   'upgraded': 'websocket',// },// webSocketConnectHeaders: {//   'upgraded': 'websocket',// },// 连接beforeConnect: beforeConnectCallback,onConnect: onConnectCallback,onDisconnect: onDisconnectCallback,onStompError: onStompErrorCallback,onUnhandledFrame: onUnhandledFrameCallback,onUnhandledMessage: onUnhandledMessageCallback,onUnhandledReceipt: onUnhandledReceiptCallback,onWebSocketError: onWebSocketErrorCallback,onWebSocketDone: onWebSocketDoneCallback,onDebugMessage: onDebugMessageCallback,));}/// beforeConnect:未来	在建立连接之前将等待的异步函数。Future<void> beforeConnectCallback() async {// 在建立连接之前将等待的异步函数。print("beforeConnectCallback 在建立连接之前将等待的异步函数。");print('waiting to connect...');// await Future.delayed(Duration(milliseconds: 200));print('connecting...');}/// onClientNotCreateCallback, client未创建void onClientNotCreateCallback() {// client未创建print("onClientNotCreateCallback client未创建");}/// onConnect:函数(StompFrame)	客户端连接成功调用的函数void onConnectCallback(StompFrame connectFrame) {// client is connected and ready// 如果连接成功print("onConnectCallback 客户端连接成功调用的函数:""${connectFrame.toString()},""${connectFrame.command},""${connectFrame.headers},""${connectFrame.body}");}/// onDisconnect:函数(StompFrame)	客户端预期断开连接时调用的函数void onDisconnectCallback(StompFrame p1) {// 客户端预期断开连接时调用的函数print("onDisconnectCallback 客户端预期断开连接时调用的函数:${p1.toString()}");}/// onStompError:函数(StompFrame)	当 stomp 服务器发送错误帧时要调用的函数void onStompErrorCallback(StompFrame p1) {// 当 stomp 服务器发送错误帧时要调用的函数print("onStompErrorCallback 当 stomp 服务器发送错误帧时要调用的函数:${p1.toString()}");}/// onUnhandledFrame:函数(StompFrame)	服务器发送无法识别的帧时调用的函数void onUnhandledFrameCallback(StompFrame p1) {// 服务器发送无法识别的帧时调用的函数print("onUnhandledFrameCallback 服务器发送无法识别的帧时调用的函数:${p1.toString()}");}/// onUnhandledMessage:函数(StompFrame)	当订阅消息没有处理程序时要调用的函数void onUnhandledMessageCallback(StompFrame p1) {// 当订阅消息没有处理程序时要调用的函数print("onUnhandledMessageCallback 当订阅消息没有处理程序时要调用的函数:${p1.toString()}");}/// onUnhandledReceipt:函数(StompFrame)	当接收消息没有注册观察者时调用的函数void onUnhandledReceiptCallback(StompFrame p1) {// 当接收消息没有注册观察者时调用的函数print("onUnhandledReceiptCallback 当接收消息没有注册观察者时调用的函数:${p1.toString()}");}/// onWebSocketError:函数(动态)	当底层 WebSocket 抛出错误时要调用的函数void onWebSocketErrorCallback(dynamic error) {// 当底层 WebSocket 抛出错误时要调用的函数print("onWebSocketErrorCallback 当底层 WebSocket 抛出错误时要调用的函数:${error.toString()}");}/// onWebSocketDone:函数()	当底层 WebSocket 完成/断开连接时要调用的函数void onWebSocketDoneCallback() {// 当底层 WebSocket 完成/断开连接时要调用的函数print("onWebSocketDoneCallback 当底层 WebSocket 完成/断开连接时要调用的函数");}/// onDebugMessage:函数(字符串)	为内部消息处理程序生成的调试消息调用的函数void onDebugMessageCallback(String p1) {// 为内部消息处理程序生成的调试消息调用的函数print("onDebugMessageCallback 为内部消息处理程序生成的调试消息调用的函数:${p1}");}// 连接void connect() {// connect连接if (_client != null) {_client?.activate();} else {// 未创建clientonClientNotCreateCallback();}}// Subscribevoid subscribe(String destination, OnFrameCallback? onFrameCallback) {if (_client != null) {dynamic unsubscribeFn = _client?.subscribe(destination: destination,headers: _headers,callback: (frame) {// Received a frame for this subscriptionprint(frame.body);if (onFrameCallback != null) {onFrameCallback(frame);}});_subscriptions.putIfAbsent(destination, () => unsubscribeFn);} else {// 未创建clientonClientNotCreateCallback();}}// client.subscribe(...) returns a function which can be called with an optional map of headersvoid unsubscribe(String destination) {if (_client != null) {dynamic unsubscribeFn = _subscriptions[destination];unsubscribeFn(unsubscribeHeaders: {});} else {// 未创建clientonClientNotCreateCallback();}}// client.subscribe(...) returns a function which can be called with an optional map of headersvoid unsubscribeAll() {// 退订所有// 调用 Map 对象的 keys 成员 , 返回一个由 键 Key 组成的数组for (var destination in _subscriptions.keys){unsubscribe(destination);}}void send(String destination, String? message) {if (_client != null) {_client?.send(destination: destination, body: message, headers: _headers);} else {// 未创建clientonClientNotCreateCallback();}}void disconnect() {if (_client != null) {_client?.deactivate();} else {// 未创建clientonClientNotCreateCallback();}}
}

2.3 使用websocketmanager收发消息

创建页面进行消息收发

class MyHomePage extends StatefulWidget {MyHomePage({Key? key, required this.title}) : super(key: key);final String title;_MyHomePageState createState() => _MyHomePageState();
}class _MyHomePageState extends State<MyHomePage> {void initState() {// TODO: implement initStatesuper.initState();}void dispose() {// TODO: implement disposesuper.dispose();}Widget build(BuildContext context) {return Scaffold(appBar: AppBar(// Here we take the value from the MyHomePage object that was created by// the App.build method, and use it to set our appbar title.title: Text(widget.title),),floatingActionButton: FloatingActionButton(onPressed: () {_incrementCounter(model);},tooltip: 'Increment',child: Icon(Icons.add),),body: Center(child: Column(mainAxisAlignment: MainAxisAlignment.center,children: <Widget>[Wrap(spacing: 8.0, // 主轴(水平)方向间距runSpacing: 4.0, // 纵轴(垂直)方向间距alignment: WrapAlignment.center, //沿主轴方向居中children: [TextButton(onPressed: stompCreate,child: Container(color: Colors.black26,child: Text('stomp创建',style: Theme.of(context).textTheme.bodyMedium,),),),TextButton(onPressed: stompConnect,child: Container(color: Colors.black26,child: Text('stomp连接',style: Theme.of(context).textTheme.bodyMedium,),),),TextButton(onPressed: stompSubscribe,child: Container(color: Colors.black26,child: Text('stomp订阅',style: Theme.of(context).textTheme.bodyMedium,),),),TextButton(onPressed: stompUnSubscribe,child: Container(color: Colors.black26,child: Text('stomp退订',style: Theme.of(context).textTheme.bodyMedium,),),),TextButton(onPressed: stompSendMessage,child: Container(color: Colors.black26,child: Text('stomp发送消息',style: Theme.of(context).textTheme.bodyMedium,),),)],),],),),);}// 测试stomp长链接void stompCreate() {// 创建stompClintWebSocketStompManager().createConnect("ws://192.168.100.25:8080/test-endpoint/websocket", {});}void stompConnect() {WebSocketStompManager().connect();}void stompSubscribe() {WebSocketStompManager().subscribe("/topic/echo", (p0) {print("stompSubscribe 1:$p0");});WebSocketStompManager().subscribe("/topic/echo", (p0) {print("stompSubscribe 2:$p0");});}void stompUnSubscribe() {WebSocketStompManager().unsubscribeAll();}void stompSendMessage() {WebSocketStompManager().send("/app/echo", "haha message from dart");}
}

至此实现了flutter开发实战-长链接WebSocket 使用stomp协议,进行消息发送、消息接收。

2.4 注意事项

由于stomp_dart_client不支持https,如果使用WebSocketStompManager().createConnect(“ws://192.168.100.25:8080/test-endpoint/websocket”, {});
会报告错误“Not support Https shceme”,所以这里要使用ws或者wss。

三、小结

至此实现了flutter开发实战-长链接WebSocket 使用stomp协议,进行消息发送、消息接收。stomp实现的库stomp_dart_client来实现该功能。

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/21857.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务实例构建成 docker 镜像实例

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

8.带你入门matlab 数据统计与分析——区间参数估计 均匀分布(matlab 程序 )

1.简述 本文将涉及到数理统计的最后一个模块——参数估计&#xff0c;后续将更新的模块是多项式计算、数据插值和曲线拟合。 在讲述使用matlab来实现参数估计之前&#xff0c;有必要去了解一些基本原理。 1.离散型随机变量的极大似然估计法: (1) 似然函数 若X为离散型, 似然函数…

SQL进阶(2)——SQL语句类型 增删改查CRUD 事务初步 表关联关系 视图 +索引

目录 引出SQL语句类型1.DML数据操纵语言&#xff08;重点&#xff09;2.DQL数据查询语言&#xff08;重点&#xff09;3.DDL(Data Definition Language了解)4.DCL(Data Control Language了解)5.TCL 事务控制语言 运算符和其他函数1.运算符2.其它函数增删改查CRUD 视图索引事务1…

Qt实现画板绘制椭圆

Qt在窗体中绘图在paintEvent函数中进行,使用QPainter类进行窗体绘制 如果只是简单的在paintevent中向画布绘制椭圆,由于实时绘制的许多个椭圆重合在一起,就会出现下面的情况 你可以在每次绘制椭圆之前调用清空画布 myPix->fill(Qt::white);但是又会出现下面的情况,无法…

UE使用UnLua(一)

一、概述 Unlua是个功能丰富的插件&#xff0c;可以在UE中进行蓝图绑定&#xff0c;在Lua中进行逻辑开发&#xff0c;使用Lua热更新的特性&#xff0c;可以快速开发迭代表层逻辑 二、UnLua环境 首先下载UnLua的插件包&#xff0c;点击下载&#xff0c;不会吧不会吧还有人打不…

计算机网络-运输层

目录 一、运输层概述 二、运输层端口号、复用和分用的概念 &#xff08;一&#xff09;端口号 &#xff08;二&#xff09;发送方的复用和接收方的分用 &#xff08;三&#xff09;TCP/IP体系的应用层常用协议所使用的运输层熟知端口号 三、UDP和TCP的区别 四、TCP的流量…

DAY46:动态规划(八)01背包应用2:一和零(二维容量01背包)

文章目录 474.一和零思路为什么不是多重背包而是01背包与之前01背包问题的区别 DP数组的含义递推公式&#xff08;也是求最大值&#xff09;初始化遍历顺序完整版总结 474.一和零 本题属于 装满背包最多能有多少个物品 类型本题是容量有两个维度的背包&#xff0c;背包容量是m…

奥特曼与钢铁侠【InsCode Stable Diffusion美图活动一期】

文章目录 简介图片生成步骤更多体验方式 简介 InsCode 是一个一站式的软件开发服务平台&#xff0c;从开发-部署-运维-运营&#xff0c;都可以在 InsCode 轻松完成。 InsCode 的 Ins 是 Inspiration&#xff0c;意思是创作、寻找有灵感的代码。 Stable Diffusion是文图生成模型…

手机快充协议

高通:QC2.0、QC3.0、QC3.5、QC4.0、QC5.0、 FCP、SCP、AFC、SFCP、 MTKPE1.1/PE2.0/PE3.0、TYPEC、PD2.0、PD3.0/3.1、VOOC 支持 PD3.0/PD2.0 支持 QC3.0/QC2.0 支持 AFC 支持 FCP 支持 PE2.0/PE1.1 联发科的PE&#xff08;Pump Express&#xff09;/PE 支持 SFCP 在PP…

narak靶机详解

narak靶机复盘 首先对靶机进行扫描&#xff0c;找到靶机的真实ip地址。 然后dirb进行目录扫描&#xff0c;扫描到一个目录&#xff0c;我们打开发现是一个登陆界面。 并没有用户名和密码&#xff0c;我们就用cewl扫描这个网站&#xff0c;扫出一个字典&#xff0c;用来暴力破…

【微信小程序创作之路】- 小程序窗口整体配置(导航栏、标题)

【微信小程序创作之路】- 小程序窗口导航栏配置 第五章 微信小程序窗口导航栏配置 文章目录 【微信小程序创作之路】- 小程序窗口导航栏配置前言一、入口文件的配置二、页面配置三、全局默认窗口配置1.navigationBarTitleText&#xff1a;导航栏标题文字2.navigationBarBackgr…

Unity 限制范围-限制在4分之一圆柱形范围内活动

在我的游戏中&#xff0c;玩家的两只手操控中&#xff0c;不想让他们的手围着自己在一个圆形范围内随便乱跑&#xff0c;左手只想让他在左上角&#xff0c;右手在右上角范围活动。所以我制作这样一个算法来实现。 首先用Dot函数划分出4个区域&#xff0c;然后根据区域计算修正…