当前位置: 首页 > news >正文

Flutter:WebSocket封装-实现心跳、重连机制

前言Permalink
Flutter简介

Flutter 是 Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App,一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件、接口,开发者可以很快地为 Flutter添加 native扩展。同时 Flutter还使用 Native引擎渲染视图,这无疑能为用户提供良好的体验。

WebSocket简介

Http协议是无状态的,只能由客户端主动发起,服务端再被动响应,服务端无法向客户端主动推送内容,并且一旦服务器响应结束,链接就会断开(见注解部分),所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术,现在已经被主流浏览器支持,所以对于Web开发者来说应该比较熟悉了,Flutter也提供了专门的包来支持WebSocket协议。

注意:Http协议中虽然可以通过keep-alive机制使服务器在响应结束后链接会保持一段时间,但最终还是会断开,keep-alive机制主要是用于避免在同一台服务器请求多个资源时频繁创建链接,它本质上是支持链接复用的技术,而并非用于实时通信,读者需要知道这两者的区别。

WebSocket协议本质上是一个基于tcp的协议,它是先通过HTTP协议发起一条特殊的http请求进行握手后,如果服务端支持WebSocket协议,则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接,和http协议不同的是,WebSocket的tcp链接是个长链接(不会断开),所以服务端与客户端就可以通过此TCP连接进行实时通信。有关WebSocket协议细节,读者可以看RFC文档,下面我们重点看看Flutter中如何使用WebSocket。

话不多说,直接撸代码Permalink
添加依赖:

web_socket_channel: ^1.1.0 # WebSocket
新建web_socket_utility.dart工具类:

import 'dart:async';import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';/// WebSocket地址
const String _SOCKET_URL = 'ws://121.40.165.18:8800';/// WebSocket状态
enum SocketStatus {SocketStatusConnected, // 已连接SocketStatusFailed, // 失败SocketStatusClosed, // 连接关闭
}class WebSocketUtility {/// 单例对象static WebSocketUtility _socket;/// 内部构造方法,可避免外部暴露构造函数,进行实例化WebSocketUtility._();/// 获取单例内部方法factory WebSocketUtility() {// 只能有一个实例if (_socket == null) {_socket = new WebSocketUtility._();}return _socket;}IOWebSocketChannel _webSocket; // WebSocketSocketStatus _socketStatus; // socket状态Timer _heartBeat; // 心跳定时器num _heartTimes = 3000; // 心跳间隔(毫秒)num _reconnectCount = 60; // 重连次数,默认60次num _reconnectTimes = 0; // 重连计数器Timer _reconnectTimer; // 重连定时器Function onError; // 连接错误回调Function onOpen; // 连接开启回调Function onMessage; // 接收消息回调/// 初始化WebSocketvoid initWebSocket({Function onOpen, Function onMessage, Function onError}) {this.onOpen = onOpen;this.onMessage = onMessage;this.onError = onError;openSocket();}/// 开启WebSocket连接void openSocket() {closeSocket();_webSocket = IOWebSocketChannel.connect(_SOCKET_URL);print('WebSocket连接成功: $_SOCKET_URL');// 连接成功,返回WebSocket实例_socketStatus = SocketStatus.SocketStatusConnected;// 连接成功,重置重连计数器_reconnectTimes = 0;if (_reconnectTimer != null) {_reconnectTimer.cancel();_reconnectTimer = null;}onOpen();// 接收消息_webSocket.stream.listen((data) => webSocketOnMessage(data),onError: webSocketOnError, onDone: webSocketOnDone);}/// WebSocket接收消息回调webSocketOnMessage(data) {onMessage(data);}/// WebSocket关闭连接回调webSocketOnDone() {print('closed');reconnect();}/// WebSocket连接错误回调webSocketOnError(e) {WebSocketChannelException ex = e;_socketStatus = SocketStatus.SocketStatusFailed;onError(ex.message);closeSocket();}/// 初始化心跳void initHeartBeat() {destroyHeartBeat();_heartBeat =new Timer.periodic(Duration(milliseconds: _heartTimes), (timer) {sentHeart();});}/// 心跳void sentHeart() {sendMessage('{"module": "HEART_CHECK", "message": "请求心跳"}');}/// 销毁心跳void destroyHeartBeat() {if (_heartBeat != null) {_heartBeat.cancel();_heartBeat = null;}}/// 关闭WebSocketvoid closeSocket() {if (_webSocket != null) {print('WebSocket连接关闭');_webSocket.sink.close();destroyHeartBeat();_socketStatus = SocketStatus.SocketStatusClosed;}}/// 发送WebSocket消息void sendMessage(message) {if (_webSocket != null) {switch (_socketStatus) {case SocketStatus.SocketStatusConnected:print('发送中:' + message);_webSocket.sink.add(message);break;case SocketStatus.SocketStatusClosed:print('连接已关闭');break;case SocketStatus.SocketStatusFailed:print('发送失败');break;default:break;}}}/// 重连机制void reconnect() {if (_reconnectTimes < _reconnectCount) {_reconnectTimes++;_reconnectTimer =new Timer.periodic(Duration(milliseconds: _heartTimes), (timer) {openSocket();});} else {if (_reconnectTimer != null) {print('重连次数超过最大次数');_reconnectTimer.cancel();_reconnectTimer = null;}return;}}
}使用方法Permalink
import 'package:my_app/utils/web_socket_utility.dart';WebSocketUtility().initWebSocket(onOpen: () {WebSocketUtility().initHeartBeat();
}, onMessage: (data) {print(data);
}, onError: (e) {print(e);
});

转自:https://ricardolsw.github.io/blog/Flutter-WebSocket%E5%B0%81%E8%A3%85-%E5%AE%9E%E7%8E%B0%E5%BF%83%E8%B7%B3-%E9%87%8D%E8%BF%9E%E6%9C%BA%E5%88%B6/

更新dart版本后的代码:

import 'dart:async';import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:kkview_kuaichuan/config.dart';
/// WebSocket状态
enum SocketStatus {socketStatusConnected, // 已连接socketStatusFailed, // 失败socketStatusClosed, // 连接关闭
}class WebSocketUtility {/// 单例对象static final WebSocketUtility _socket = WebSocketUtility._internal();/// 内部构造方法,可避免外部暴露构造函数,进行实例化WebSocketUtility._internal();/// 获取单例内部方法factory WebSocketUtility() {return _socket;}late WebSocketChannel _webSocket; // WebSocketSocketStatus? _socketStatus; // socket状态Timer? _heartBeat; // 心跳定时器final int _heartTimes = 30000; // 心跳间隔(毫秒)final int _reconnectCount = 2; // 重连次数,默认60次int _reconnectTimes = 0; // 重连计数器Timer? _reconnectTimer; // 重连定时器late Function onError; // 连接错误回调late Function onOpen; // 连接开启回调late Function onMessage; // 接收消息回调/// 初始化WebSocketvoid initWebSocket({required Function onOpen, required Function onMessage, required Function onError}) {this.onOpen = onOpen;this.onMessage = onMessage;this.onError = onError;openSocket();}/// 开启WebSocket连接void openSocket() {// closeSocket();_webSocket = WebSocketChannel.connect(Uri.parse(SIGNALSERVERURL));print('WebSocket连接成功: $SIGNALSERVERURL');// 连接成功,返回WebSocket实例_socketStatus = SocketStatus.socketStatusConnected;// 连接成功,重置重连计数器_reconnectTimes = 0;if (_reconnectTimer != null) {_reconnectTimer?.cancel();_reconnectTimer = null;}onOpen();// 接收消息_webSocket.stream.listen((data) => webSocketOnMessage(data),onError: webSocketOnError, onDone: webSocketOnDone);}/// WebSocket接收消息回调webSocketOnMessage(data) {onMessage(data);}/// WebSocket关闭连接回调webSocketOnDone() {print('webSocketOnDone closed');_socketStatus = SocketStatus.socketStatusClosed;reconnect();}/// WebSocket连接错误回调webSocketOnError(e) {WebSocketChannelException ex = e;_socketStatus = SocketStatus.socketStatusFailed;onError(ex.message);closeSocket();}/// 初始化心跳void initHeartBeat() {destroyHeartBeat();_heartBeat =Timer.periodic(Duration(milliseconds: _heartTimes), (timer) {sentHeart();});}/// 心跳void sentHeart() {sendMessage('{"module": "HEART_CHECK", "message": "请求心跳"}');}/// 销毁心跳void destroyHeartBeat() {if (_heartBeat != null) {_heartBeat?.cancel();_heartBeat = null;}}/// 关闭WebSocketvoid closeSocket() {print('WebSocket连接关闭');_webSocket.sink.close();destroyHeartBeat();_socketStatus = SocketStatus.socketStatusClosed;}/// 发送WebSocket消息void sendMessage(message) {switch (_socketStatus) {case SocketStatus.socketStatusConnected:print('发送中:$message');_webSocket.sink.add(message);break;case SocketStatus.socketStatusClosed:print('连接已关闭');break;case SocketStatus.socketStatusFailed:print('发送失败');break;default:break;}}/// 重连机制void reconnect() {if (_reconnectTimes < _reconnectCount) {_reconnectTimes++;_reconnectTimer =Timer.periodic(Duration(milliseconds: _heartTimes), (timer) {openSocket();});} else {if (_reconnectTimer != null) {print('重连次数超过最大次数');_reconnectTimer?.cancel();_reconnectTimer = null;}return;}}get socketStatus => _socketStatus;get webSocketCloseCode => _webSocket.closeCode;}

相关文章:

Flutter:WebSocket封装-实现心跳、重连机制

前言Permalink Flutter简介 Flutter 是 Google推出并开源的移动应用开发框架&#xff0c;主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App&#xff0c;一套代码同时运行在 iOS 和 Android平台。 Flutter提供了丰富的组件、接口&#xff0c;开发者可以很快地为 F…...

c语言中:struct timespec

在C语言中&#xff0c;struct timespec 是一个结构体&#xff0c;通常用于处理时间和时间间隔。这个结构体通常包含以下两个成员&#xff1a; tv_sec&#xff1a;这是一个长整型&#xff08;long&#xff09;&#xff0c;用于存储秒数。它表示时间的整数部分&#xff0c;即秒数…...

Mendix如何实现导出文件

刚刚接触Mendix低代码两周&#xff0c;花了一周在b站看初级视频然后考完初级&#xff0c;第二周开始做个列表查询感觉照葫芦画瓢没啥难度。但最近要求写个导出列表数据&#xff0c;在mendix社区翻了翻&#xff0c;这个功能算是常见的。找了mendix官方提供的Docs磕磕盼盼才实现了…...

在IIS服务器上安装SSL证书(2023配置启用HTTPS部署教程)内容来源SSL市场网

https://www.sslmarket.com.cn/146.html...

如何处理ChatGPT与用户之间的互动和反馈?

处理ChatGPT与用户之间的互动和反馈是关于改进和优化用户体验的关键方面。这涉及到在聊天、对话和交互中建立积极的用户关系&#xff0c;同时利用用户的反馈来不断改进ChatGPT的性能和功能。本文将探讨如何有效地处理ChatGPT与用户之间的互动和反馈&#xff0c;以提供更好的用户…...

微服务-gateway鉴权

文章目录 一、前言二、gateway鉴权1、依赖配置2、编写代码3、GlobalFilter详解3.1、GlobalFilter简介3.2、GlobalFilter自定义执行顺序3.2.1、实现Order接口实现自定义执行顺序 一、前言 网关是介于客户端和服务器端之间的中间层&#xff0c;所有的外部请求都会先经过 网关这一…...

NET7快速开发一个商品管理模块-商品列表开发(一)

商品管理模块&#xff0c;一般包含以下几个模块&#xff1a; 商品列表&#xff1a;这里可以看到所有已发布的商品信息列表。 商品管理&#xff1a;添加商品、编辑商品以及删除商品。 具体功能如下图&#xff1a; 1.商品列表 2.添加商品 3.商品SKU编辑...

0829|C++day7 auto、lambda、C++数据类型转换、C++标准模板库(STL)、list、文件操作

一、思维导图 二、【试编程】将实例化类对象写入容器后&#xff0c;写入.txt文本中&#xff0c;再重新定义一个类容器&#xff0c;将.txt中的内容读取出来&#xff0c;输出到终端 封装一个学生的类&#xff0c;定义一个学生这样类的vector容器, 里面存放学生对象&#xff08;至…...

SpringBoot连接MySQL数据库实例【tk.mybatis连接mysql数据库】

文章目录 一、数据库表二、引入依赖三、修改配置文件四、公共组件1、BaseController2、BaseService3、IService4、BaseMapper 五、代码1、Application2、Student实体类3、Controller4、Service5、ServiceImpl6、Mapper7、Mapper.xml 一、数据库表 CREATE TABLE student (id i…...

node基础之三:http 模块

// 1. 导入模块 const http require("http"); // 2. 创建服务 const server http.createServer((request, response) > {// 获取请求方法request.method;// 获取请求 url&#xff08;只包含url中的路径和查询字符串&#xff09;request.url;// 获取 HTTP 协议版…...

【高阶数据结构】AVL树 {概念及实现;节点的定义;插入并调整平衡因子;旋转操作:左单旋,右单旋,左右双旋,右左双旋;AVL树的验证及性能分析}

AVL树 一、AVL树的概念 二叉搜索树虽可以缩短查找的效率&#xff0c;但如果数据有序或接近有序二叉搜索树将退化为单支树&#xff0c;查找元素相当于在顺序表中搜索元素&#xff0c;效率低下。因此&#xff0c;两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明…...

Netty—FuturePromise

Netty—Future&Promise 一、JDK原生 Future二、Netty包下的 Future三、Promise1、使用Promise同步获取结果2、使用Promise异步获取结果.3、使用Promise同步获取异常 - sync & get4、使用Promise同步获取异常 - await5、使用Promise异步获取异常 在异步处理时&#xff0…...

固定资产卡片乱怎么管理

固定资产卡片是记录公司固定资产信息的重要工具&#xff0c;如果管理不善&#xff0c;容易造成卡片混乱、数据错误等问题。 为了避免这种情况的发生&#xff0c;可以采取以下措施&#xff1a;  建立完善的资产管理制度&#xff0c;明确固定资产的分类、标准和使用情况&#x…...

AutoHotkey(AHK)脚本,自动截图当前屏幕并发送给微信窗口

前提先安装好 AutoHotkey &#xff0c;本脚本依赖AutoHotkey 环境 首先 &#xff0c;设置微信的快捷键 执行代码如下&#xff1a; Loop {SendInput, {Alt down}s{Alt up}Sleep, 2000 ; 等待2秒; 双击鼠标左键Click, 2Sleep, 1000 ; 等待1秒SendInput, {Alt down}a{Alt up}Sl…...

Golang - go build打包文件

Go编译打包文件 1、简单打包 程序 main1.go&#xff1a; package mainimport "fmt"func main() {fmt.Println("Hello World!") } 打包&#xff1a; # 在linux服务上执行下面的3个命令 # linux平台,生成main1可执行程序 CGO_ENABLED0 GOOSlinux GOARCHam…...

Java的归并排序

不爱生姜不吃醋⭐️⭐️⭐️ 如果本文有什么错误的话欢迎在评论区中指正 与其明天开始&#xff0c;不如现在行动&#xff01; 文章目录 &#x1f334;前言&#x1f334;一.归并排序1.概念2.时间复杂度3.代码实现 &#x1f334;二、小和问题1.概念2.举例3.代码实现 &#x1f334…...

B. The Walkway Codeforces Round 893 (Div. 2)

Problem - B - Codeforces 题目大意&#xff1a;小明在数轴上要从1走到n&#xff0c;其中某些坐标上有一些饼干店&#xff0c;共m个&#xff0c;小明身上也有无限多的饼干&#xff0c;它首先一定会在1的位置吃一个饼干&#xff0c;在每个饼干店的位置会吃一个&#xff0c;在前…...

第四篇 DirectShow 采集调用结构关系

第一篇: DirectShow视频采集_会头痛的可达鸭的博客-CSDN博客 一、GraphBuilder 1、IFilterGraph2、IGraphBuilder、ICaptureGraphBuiler2 (1)、CLSID IFilterGraph CLSID_FilterGraphIFilterGraph2 CLSID_CaptureGraphBuilderIGraphBuilder CL…...

2605. 从两个数字数组里生成最小数字

文章目录 Tag题目来源题目解读解题思路方法一&#xff1a;枚举比较法方法二&#xff1a;集合的位运算表示法 写在最后 Tag 【贪心】【位运算】【数组】 题目来源 2605. 从两个数字数组里生成最小数字 题目解读 给定两个各自只包含数字 1 到 9 的两个数组&#xff0c;每个数组…...

服务器发送事件Server-sent events详解与示例

Server-sent events 服务端进行数据推送除了WebSocket之外&#xff0c;还可以使用Server-Send-Event方案。 与 WebSocket不同的是&#xff0c;服务器发送事件是单向的。数据消息只能从服务端到发送到客户端&#xff08;如用户的浏览器&#xff09;。这使其成为不需要从客户端…...

SOLIDWORKS 多实体的建模方式

SOLIDWORKS多实体是SOLIDWORKS中一个非常有用的功能。在SOLIDWORKS中&#xff0c;对于模型的设定通常被大家所熟知的有以下几种类型&#xff1a;零件、装配体以及工程图。 其实还有一种划分&#xff0c;就是多实体。严格意义上来说&#xff0c;多实体既不属于零件也不属于装配体…...

NSSCTF web 刷题记录1

文章目录 前言题目[GXYCTF 2019]禁止套娃方法一方法二 [NCTF 2019]Fake XML cookbook[NSSRound#7 Team]ec_RCE[NCTF 2018]Flask PLUS 前言 今天是2023.9.3&#xff0c;大二开学前的最后一天。老实说ctf的功力还是不太够做的题目太少&#xff0c;新学期新气象。不可急于求成&am…...

遥感指数数据库

目前遥感指数多种多样&#xff0c;那怎么针对不同的应用领域选择合适的植被指数&#xff1f;不同卫星又有哪些可以反演的指数&#xff1f; Henrich等人开发了Index Database(网址&#xff1a;https://www.indexdatabase.de/)&#xff0c;总结了目前主流的遥感指数&#xff0c;…...

如何让insert程序速度快,可以试试联合SQL(insert 和 select 一起使用)?

查询添加可选择SQL执行&#xff0c;速度远超程序执行 insert 和 select案例 insert into 表1(列1,列2,列3,...) select 列1,列2,列3,...from表2(GROUP BY 列)116511 条数据 耗时45秒&#xff0c; 如果是程序查询然后再insert&#xff0c;则需要30分钟左右&#xff01;&#x…...

IP地址、网关、网络/主机号、子网掩码关系

一、IP地址 IP地址组成 IP地址分为两个部分&#xff1a;网络号和主机号 &#xff08;1&#xff09;网络号:标识网段&#xff0c;保证相互连接的两个网段具有不同的标识。 &#xff08;2&#xff09;主机号:标识主机&#xff0c;同一网段内&#xff0c;主机之间具有相同的网…...

使用skvideo.io.vread读取avi视频,报错“No way to determine width or height from video...”

问题描述&#xff1a; 一开始安装sk-video&#xff0c;在使用skvideo.io.vread读取avi视频&#xff0c;报错“No way to determine width or height from video. Need -s in inputdict. Consult documentation on I/O.” 解决方案&#xff1a; 1. 卸载sk-video pip uninsta…...

Nomad 系列-安装

系列文章 Nomad 系列文章 Nomad 简介 开新坑&#xff01;近期算是把自己的家庭实验室环境初步搞好了&#xff0c;终于可以开始进入正题研究了。 首先开始的是 HashiCorp Nomad 系列&#xff0c;欢迎阅读。 关于 Nomad 的简介&#xff0c;之前在 大规模 IoT 边缘容器集群管…...

网络版五子棋C++实现

目录 1.项目介绍 2.开发环境 3.核心技术 4.环境搭建 5.WebSocketpp介绍 5.1WebSocketpp是什么 5.2为什么使用WebSocketpp 5.3原理解析&#xff1a; 5.4WebSocketpp主要特性 6.WebSocketpp使用 7.JsonCpp使用 8.MySQL API 9.项目模块设计以及流程图 10.封装日志宏…...

项目招标投标公众号小程序开源版开发

项目招标投标公众号小程序开源版开发 以下是一个招标投标公众号小程序的功能列表&#xff1a; 用户注册与登录&#xff1a;用户可以注册账号并登录公众号小程序。项目发布&#xff1a;用户可以发布招标项目的详细信息&#xff0c;包括项目名称、招标单位、项目描述、招标要求…...

华为OD机试-机器人走迷宫

题目描述 机器人走一个迷宫,给出迷宫的x和y(x*y的迷宫)并且迷宫中有障碍物,输入k表示障碍物有k个,并且会将障碍物的坐标挨个输入. 机器人从0,0的位置走到x,y的位置并且只能向x,y增加的方向走,不能回退. 如代码类注释展示的样子,#表示可以走的方格,0代表障碍,机器人从0,0的位置…...