草庐IT

端开发技术—FLutter开发即时通讯

咸鱼杰克 2023-03-28 原文

1. 即时通讯简述

即时通讯是端开发工作中常见的需求,本篇文章以作者工作中使用FLutter开发社交软件即时通讯需求为背景,描述一下即时通讯功能设计的要点。

2. 重要概念

即时通讯需要前后端配合,约定消息格式与消息内容。本次IM客户端需求开发使用了公司已有的基于Socket.io搭建的后台,下文描述涉及到的一些概念。

2.1 WebSocket协议

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket协议与传统的HTTP协议的主要区别为,WebSocket协议允许服务端主动向客户端推送数据,而传统的HTTP协议服务器只有在客户端主动请求之后才能向客户端发送数据。在没有WebSocket之前,即时通讯大部分采用长轮询方式。

2.2 Socket.io和WebSocket的区别

Socket.io不是WebSocket,它只是将WebSocket和轮询 (Polling)机制以及其它的实时通信方式封装成了通用的接口,并且在服务端实现了这些实时机制的相应代码。也就是说,WebSocket仅仅是Socket.io实现即时通信的一个子集。因此WebSocket客户端连接不上Socket.io服务端,当然Socket.io客户端也连接不上WebSocket服务端。

2.3 服务端socket消息

理解了服务端socket消息也就理解了服务器端的即时通讯逻辑,服务器发出的socket消息可以分为两种:

  1. 服务器主动发出的消息:

    例如,社交软件中的A用户给B用户发出了消息,服务器在收到A用户的消息后,通过socket链接,将A用户的消息转发给B用户,B用户客户端接收到的消息就属于服务器主动发出的。其他比较常见的场景例如直播软件中,全平台用户都会收到的礼物消息广播。

  2. 服务器在接收到客户端消息后的返回消息:

    例如,长链接心跳机制,客户端向服务器发送ping消息,服务器在成功接受客户端的ping消息后返回的pong消息就属于服务器的返回消息。其他常见的场景如社交软件中A用户给B用户发出了消息,服务器在收到A用户的消息后,给A客户端返回一条消息,供A客户端了解消息的发送状态,判断发送是否成功。大部分场景,服务器在接收到客户端主动发出的消息之后都需要返回一条消息。

3. 客户端实现流程

几个设计客户端即时通讯的重点。

3.1 心跳机制

所谓心跳就是客户端发出ping消息,服务器成功收到后返回pong消息。当客户端一段时间内不在发送ping消息,视为客户端断开,服务器就会主动关闭socket链接。当客户端发送ping消息,服务器一段时间内没有返回pong消息,视为服务器断开,客户端就会启动重连机制。

3.2 重连机制

重连机制为客户端重新发起连接,常见的重连条件如下:

  • 客户端发送ping消息,服务器一段时间内没有返回pong。
  • 客户端网络断开。
  • 服务器主动断开连接。
  • 客户端主动连接失败。

当出现极端情况(客户端断网)时,频繁的重连可能会导致资源的浪费,可以设置一段时间内的最大重连次数,当重连超过一定次数时,休眠一段时间。

3.3 消息发送流程

  1. 将消息存储到本地数据库,发送状态设为等待。
  2. 发送socket消息。
  3. 接收到服务器返回的socket消息后,将本地数据库等待状态的消息改为成功。

注意事项:

将消息存储到本地数据库时需要生成一个id存入数据库,同时传给服务器,当收到消息时根据id判断更新本地数据库的哪一条消息。

3.4 消息接收流程

3.5 其他相关

  • 聊天页消息的排序:在查询本地数据库时使用order by按时间排序。
  • 消息列表:也推荐做本地存储,当收到消息的时候需要先判断本地消息列表是否有当前消息用户的对话框,如果没有就先插入,有就更新。消息列表的维护就不展开说了,感兴趣可以看代码。
  • 图片语音消息:将图片和语言先上传到专门的服务器上(各种专门的云存储服务器),sokcet消息和本地存储传递的是云服务器上的URL。
  • 多人聊天(群聊):与单人聊天逻辑基本一致,区别位本地数据库需要添加一个会话ID字段,打开一个群就查询对应会话ID的数据。聊天消息不再是谁发给谁,而是在哪个群聊下。

4. 客户端Flutter代码

把部分代码贴上来,完整项目在作者的github上。

4.1 心跳机制

  heart() {
    pingTimer = Timer.periodic(Duration(seconds: 30), (data) {
      if (pingWaitTime >= 60) {
        socket.connect();
        pingWaitTime = 0;
        pingWaitTimer!.cancel();
        ping();
      }
      if (!pingWaitFlag) ping();
    });
  }

  ping() {
    debugPrint("ping");
    String pingData =
        '{"type":"ping","payload":{"front":true},"msg_id":${DateTime.now().millisecondsSinceEpoch}}';
    socket.emit("message", pingData);
    pingWaitFlag = true;
    pingWaitTime = 0;
    pingWaitTimer = Timer.periodic(Duration(seconds: 1), (data) {
      pingWaitTime++;
      print(data.hashCode);
      if (pingWaitTime % 10 == 0) debugPrint(pingWaitTime.toString());
    });
  }
	//pong
  if (socketMessage.type == PONG && socketMessage.code == 1000) {
        pingWaitFlag = false;
        pingWaitTimer!.cancel();
        pingWaitTime = 0;
      }

4.2 本地数据库设计

数据库表的设计是比较重要的,理解了数据库设计,读代码也就无压力了。

      //消息表
      CREATE TABLE chatDetail (
         chat_id TEXT PRIMARY KEY,//主键
         from_id TEXT,//发送人
         to_id TEXT,//接收人
         created_at TEXT,
         content TEXT,//消息内容
         image TEXT,//UI展示用,用户头像
         name TEXT,//UI展示用,用户名
         sex TEXT,//UI展示用,用户性别
         status TEXT,//消息状态
         type INTEGER,//消息类型,图片/文字/语音等
         chat_object_id TEXT//聊天对象ID,对当前用户而言的聊天对象,是一系列本地操作的核心
         )
       //消息列表表
       CREATE TABLE chatList (
         cov_id TEXT,
         unread_count INTEGER,
         last_msg_text TEXT,
         last_msg_at TEXT,
         image TEXT,
         name TEXT,
         sex TEXT,
         chat_object_id TEXT PRIMARY KEY)

5. 总结

无论是Flutter技术,或是IOS/Android/Web。只要掌握了即时通讯的核心开发流程,不同的技术只是API有些变化。API往往看文档就能解决,大前端或是特定平台的工程师还是要掌握核心开发流程,会几种做同样事情的API意义不大。

demo写的比较简单,有问题可以评论。

项目github地址

有关端开发技术—FLutter开发即时通讯的更多相关文章

  1. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  2. ruby - 即时确定方法的可见性 - 2

    我正在编写一个方法,它将在一个类中定义一个实例方法;类似于attr_accessor:classFoocustom_method(:foo)end我通过将custom_method函数添加到Module模块并使用define_method定义方法来实现它,效果很好。但我无法弄清楚如何考虑类(class)的可见性属性。例如,在下面的类中classFoocustom_method(:foo)privatecustom_method(:bar)end第一个生成的方法(foo)必须是公共(public)的,第二个(bar)必须是私有(private)的。我怎么做?或者,如何找到调用我的cust

  3. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  4. ruby - 是否可以覆盖 gemfile 进行本地开发? - 2

    我们的git存储库中目前有一个Gemfile。但是,有一个gem我只在我的环境中本地使用(我的团队不使用它)。为了使用它,我必须将它添加到我们的Gemfile中,但每次我checkout到我们的master/dev主分支时,由于与跟踪的gemfile冲突,我必须删除它。我想要的是类似Gemfile.local的东西,它将继承从Gemfile导入的gems,但也允许在那里导入新的gems以供使用只有我的机器。此文件将在.gitignore中被忽略。这可能吗? 最佳答案 设置BUNDLE_GEMFILE环境变量:BUNDLE_GEMFI

  5. ruby - 在 Windows 机器上使用 Ruby 进行开发是否会适得其反? - 2

    这似乎非常适得其反,因为太多的gem会在window上破裂。我一直在处理很多mysql和ruby​​-mysqlgem问题(gem本身发生段错误,一个名为UnixSocket的类显然在Windows机器上不能正常工作,等等)。我只是在浪费时间吗?我应该转向不同的脚本语言吗? 最佳答案 我在Windows上使用Ruby的经验很少,但是当我开始使用Ruby时,我是在Windows上,我的总体印象是它不是Windows原生系统。因此,在主要使用Windows多年之后,开始使用Ruby促使我切换回原来的系统Unix,这次是Linux。Rub

  6. ruby-on-rails - 在 Rails 开发环境中为 .ogv 文件设置 Mime 类型 - 2

    我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain

  7. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  8. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  9. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  10. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

随机推荐