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地址。