Flutter连接websocket、实现在线聊天功能

老规矩效果图:

第一步:引入

web_socket_channel: ^2.4.0

第二步:封装 websocket.dart 单例

import 'dart:async';
import 'dart:convert';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/io.dart';

class WebSocketManager {
  late WebSocketChannel _channel;
  final String _serverUrl; //ws连接路径
  final String _accessToken; //登录携带的token
  bool _isConnected = false; //连接状态
  bool _isManuallyDisconnected = false; //是否为主动断开
  late Timer _heartbeatTimer; //心跳定时器
  late Timer _reconnectTimer; //重新连接定时器
  Duration _reconnectInterval = Duration(seconds: 5); //重新连接间隔时间
  StreamController<String> _messageController = StreamController<String>();

  Stream<String> get messageStream => _messageController.stream; //监听的消息

  //初始化
  WebSocketManager(this._serverUrl, this._accessToken) {
    print('初始化');
    _heartbeatTimer = Timer(Duration(seconds: 0), () {});
    _startConnection();
  }

  //建立连接
  void _startConnection() async {
    try {
      _channel = WebSocketChannel.connect(Uri.parse(_serverUrl));
      print('建立连接');
      _isConnected = true;
      _channel.stream.listen(
        (data) {
          _isConnected = true;
          print('已连接$data');
          final jsonObj = jsonDecode(data); // 将消息对象转换为 JSON 字符串
          if (jsonObj['cmd'] == 0) {
            _startHeartbeat(); //开始心跳
          } else if (jsonObj['cmd'] == 1) {
            _resetHeartbeat(); // 重新开启心跳定时
          } else {
            _onMessageReceived(data);// 其他消息转发出去
          }
        },
        onError: (error) {
          // 处理连接错误
          print('连接错误: $error');
          _onError(error);
        },
        onDone: _onDone,
      );
      _sendInitialData(); // 连接成功后发送登录信息();
    } catch (e) {
      // 连接错误处理
      print('连接异常错误: $e');
      _onError(e);
    }
  }

  //断开连接
  void disconnect() {
    print('断开连接');
    _isConnected = false;
    _isManuallyDisconnected = true;
    _stopHeartbeat();
    _messageController.close();
    _channel.sink.close();
  }

  //开始心跳
  void _startHeartbeat() {
    _heartbeatTimer = Timer.periodic(Duration(seconds: 20), (_) {
      sendHeartbeat();
    });
  }

  //停止心跳
  void _stopHeartbeat() {
    _heartbeatTimer.cancel();
  }

  //重置心跳
  void _resetHeartbeat() {
    _stopHeartbeat();
    _startHeartbeat(); //开始心跳
  }

  // 发送心跳消息到服务器
  void sendHeartbeat() {
    if (_isConnected) {
      final message = {"cmd": 1, "data": {}};
      final jsonString = jsonEncode(message); // 将消息对象转换为 JSON 字符串
      _channel.sink.add(jsonString); // 发送心跳
      print('连接成功发送心跳消息到服务器$message');
    }
  }

  // 登录
  void _sendInitialData() async {
    try {
      final message = {
        "cmd": 0,
        "data": {"accessToken": _accessToken}
      };
      final jsonString = jsonEncode(message); // 将消息对象转换为 JSON 字符串
      _channel.sink.add(jsonString); // 发送 JSON 字符串
      print('连接成功-发送登录信息$message');
    } catch (e) {
      // 连接错误处理
      print('连接异常错误: $e');
      _onError(e);
    }
  }

  //发送信息
  void sendMessage(dynamic message) {
    final data = {
      "cmd":3,
      "data":message
    };
    final jsonString = jsonEncode(data); // 将消息对象转换为 JSON 字符串
    _channel.sink.add(jsonString); // 发送 JSON 字符串
  }

  // 处理接收到的消息
  void _onMessageReceived(dynamic message) {
    print(
        '处理接收到的消息Received===========================================: $message');
    _messageController.add(message);
  }

  //异常
  void _onError(dynamic error) {
    // 处理错误
    print('Error: $error');
    _isConnected = false;
    _stopHeartbeat();
    if (!_isManuallyDisconnected) {
      // 如果不是主动断开连接,则尝试重连
      _reconnect();
    }
  }

  //关闭
  void _onDone() {
    print('WebSocket 连接已关闭');
    _isConnected = false;
    _stopHeartbeat();
    if (!_isManuallyDisconnected) {
      // 如果不是主动断开连接,则尝试重连
      _reconnect();
    }
  }

  // 重连
  void _reconnect() {
    // 避免频繁重连,启动重连定时器
    _reconnectTimer = Timer(_reconnectInterval, () {
      _isConnected = false;
      _channel.sink.close(); // 关闭之前的连接
      print('重连==============&

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

森林消防—高扬程水泵:守护绿色屏障的专业利器/恒峰智慧科技

在广袤的森林中&#xff0c;火灾无疑是最具破坏性的灾难之一。为了及时应对森林火灾&#xff0c;保护珍贵的自然资源和生态平衡&#xff0c;高效的消防设备显得尤为重要。森林消防高扬程水泵便是其中一款专业设备&#xff0c;以其高效输送水源的能力&#xff0c;成为守护森林绿…

Denoising diffusion models for out-of-distribution detection

Denoising diffusion models for out-of-distribution detection 摘要1 介绍2 相关工作2.1 基于生成得方法2.2 基于重构的方法3 方法3.1.扩散模型3.2.多次重建3.3.相似性评估4实验4.1. Experimental details4.2. Results for computer vision datasets4.3医学数据集上的结果4.4…

python 12实验

1.导入数据。 2.清洗数据&#xff0c;将缺失值或“NAN”替换为“无”&#xff0c;并将文本数据转换为数值型数据。 3.使用聚类算法&#xff08;如KMeans&#xff09;对数据进行聚类&#xff0c;并计算样本到簇中心的平均距离以确定最佳的簇数量。 4.对数据进行PCA降维&#xff…

Django Admin后台管理:高效开发与实践

title: Django Admin后台管理&#xff1a;高效开发与实践 date: 2024/5/8 14:24:15 updated: 2024/5/8 14:24:15 categories: 后端开发 tags: DjangoAdmin模型管理用户认证数据优化自定义扩展实战案例性能安全 第1章&#xff1a;Django Admin基础 1.1 Django Admin简介 Dj…

AI预测福彩3D第10套算法实战化赚米验证第1弹2024年5月5日第1次测试

从今天开始&#xff0c;准备启用第10套算法&#xff0c;来验证下本算法的可行性。因为本算法通过近三十期的内测&#xff08;内测版没有公开预测结果&#xff09;&#xff0c;发现本算法的预测结果优于其他所有算法的效果。彩票预测只有实战才能检验是否有效&#xff0c;只有真…

旅游出行大热!景区电话却打不通了?

根据文化和旅游部5月6日发布的数据显示,今年“五一”假期,全国国内旅游出游合计2.95亿人次。 这个数据可以看出出游的热度是非常高的&#xff0c;但有网友表示在旅游的时候遇到糟心的事情&#xff0c;比如无法联系到景区&#xff0c;网友吐槽自己打电话20次仅仅接通了一次&…

前端奇怪面试题总结

面试题总结 不修改下面的代码进行正常解构 这道题考的是迭代器和生成器的概念 let [a,b] {a:1,b:2}答案 对象缺少迭代器&#xff0c;需要手动加上 Object.prototype[Symbol.iterator] function* (){// return Object.values(this)[Symbol.iterator]()return yeild* Object.v…

场外期权个股怎么对冲?

今天期权懂带你了解场外期权个股怎么对冲&#xff1f;场外个股期权是一种在非交易所市场进行的期权交易&#xff0c;它允许投资者针对特定的股票获得未来买入或卖出的权利。 场外期权个股怎么对冲&#xff1f; 持有相反方向的期权&#xff1a;这是最直接的对冲方法&#xff0c…

一分钟教你学浪app视频怎么缓存

你是否在学浪app上苦苦寻找如何缓存视频的方法&#xff1f;你是否想快速、轻松地观看自己喜欢的视频内容&#xff1f;那么&#xff0c;让我们一起探索一分钟教你如何缓存学浪app视频的技巧吧&#xff01; 学浪下载工具我已经打包好了&#xff0c;有需要的自己下载一下 学浪下…

【数据分享】2006—2022年我国城市级别的市政设施水平相关指标(免费获取)

市政公用设施水平&#xff0c;作为衡量一座城市基础设施建设情况的核心指标之一&#xff0c;其完善程度、运行效率以及服务质量&#xff0c;不仅直接关乎城市的日常运转与居民生活质量&#xff0c;更是评估城市综合竞争力、宜居性以及可持续发展能力的关键要素。 我们发现在《…

unity-C#调用百度千帆AppBuilder的OpenApi

目录 功能描述准备工作百度智能云账号创建应用编辑应用创建Api秘钥Api调用流程unity代码Unitywebrequest非流式流式注意事项 Restsharp 功能描述 使用百度千帆AppBuilder平台,通过api调用的方式实现AI大模型对话功能(文字) 准备工作 百度智能云账号 请自行在百度智能云进行…

001_Langchain

LangChain LangChain 是一个开源框架,旨在帮助开发者使用大型语言模型(LLMs)和聊天模型构建端到端的应用程序。它提供了一套工具、组件和接口,以简化创建由这些模型支持的应用程序的过程。LangChain 的核心概念包括组件(Components)、链(Chains)、模型输入/输出(Mode…

Failed to build flash-attn:ERROR: Could not build wheels for flash-attn

安装 FlashAttention 的时候遇到报错&#xff1a; Failed to build flash-attn ERROR: Could not build wheels for flash-attn, which is required to install pyproject.toml-based projects可能是安装的版本与环境存在冲突吧&#xff0c;我的环境是&#xff1a; python 3.1…

GRU模块:nn.GRU层的介绍

如果需要深入理解GRU的话&#xff0c;那么内部实现的详细代码和计算公式就比较重要&#xff0c;中间的一些过程和变量的意义需要详细关注&#xff0c;只有这样&#xff0c;才能准备把握这个模块的内涵和意义&#xff0c;设计初衷和使用方式等等&#xff0c;所以&#xff0c;仔细…

值得推荐的多款iPaaS工具

当今企业面临着日益复杂的数据和系统集成挑战&#xff0c;为了提高业务效率和灵活性&#xff0c;许多企业转向了iPaaS工具&#xff08;Integration Platform as a Service&#xff0c;即集成平台即服务&#xff09;。iPaaS工具可以帮助企业轻松地连接和集成各种应用程序、数据和…

如何切换PHP版本

如果服务器上安装了多个php&#xff0c;可能会导致默认的php版本错误&#xff0c;无法启动swoole等服务&#xff0c; 查看命令行的php版本方法&#xff1a;https://q.crmeb.com/thread/9921 解决方法如下&#xff0c;选一个即可&#xff1a; 一、切换命令行php版本&#xff…

servlet-会话(cookie与session)

servlet会话技术 会话技术cookie创建Cookieindex.jspCookieServlet 获取Cookieindex.jspshowCookie session创建sessionindex.jsplogin.jspLoginServlet 获取sessionRedurectServket 清除会话login.jspClearItmeServlet 会话技术 两种会话&#xff1a;cookie&#xff0c;sessi…

在 Linux 中创建文件

目录 ⛳️推荐 前言 使用 touch 命令创建一个新的空文件 使用 echo 命令创建一个新文件 使用 cat 命令创建新文件 测试你的知识 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到…

利用Python简单操作MySQL数据库,轻松实现数据读写

PyMySQL是Python编程语言中的一个第三方模块&#xff0c;它可以让Python程序连接到MySQL数据库并进行数据操作。它的使用非常简单&#xff0c;只需要安装PyMySQL模块&#xff0c;然后按照一定的步骤连接到MySQL数据库即 可。本文将介绍PyMySQL的安装、连接MySQL数据库、创建表、…

嗨动PDF编辑器V1.60版本发布,有哪些亮点值得注意!

嗨动PDF编辑器V1.60版发布&#xff0c;有哪些亮点值得注意呢&#xff1f; 在数字信息爆炸的时代&#xff0c;PDF文档以其跨平台、易于阅读和保持格式统一的特性&#xff0c;成为了工作、学习和生活中的常客。但很多时候&#xff0c;我们收到的PDF文档只是“只读”的&#xff0…