今天看啥  ›  专栏  ›  backlight

leancloud + react-native实时通信问题整理

backlight  · 掘金  ·  · 2018-07-25 03:57

本文由我们团队肖建朋编写

技术背景:使用leancloud后端存储技术和实时通信SDK、react-native开发手机APP

签名验证

开启签名验证设置: 控制台 > 消息 > 实时消息 > 设置 > 实时消息选项 勾选要开启的签名认证 ,如图:

对于不同操作需要实现不同的signature 工厂方法:signatureFactoryconversationSignatureFactory

如果使用leancloud内置的用户鉴权系统连接实时通信服务器,可以省掉登录签名操作。这里使用了内置用户鉴权系统所以只需要对话操作签名验证conversationSignatureFactory

根据sdk文档说明conversationSignatureFactory 需要参数 有:

  1. conversationId : 对话Id
  2. clientId: 当前用户Id
  3. targetIds: 此次操作的目标用户 IDs
  4. action: 此次行为的动作,可能的值为 create(创建对话)、add(加群和邀请)和 remove(踢出群)之一(这里的动作有问题,实际和文档中描述有差异)

客户端签名实现

var signatureFactory = function(clientId) {
  return AV.Cloud.rpc('sign', { clientId: clientId }); // AV.Cloud.rpc returns a Promise
};
var conversationSignatureFactory = function(conversationId, clientId, targetIds, action) {
  return AV.Cloud.rpc('sign-conversation', {
    conversationId: conversationId,
    clientId: clientId,
    targetIds: targetIds,
    action: action,
  });
};

realtime.createIMClient('Tom', {
  signatureFactory: signatureFactory,
  conversationSignatureFactory: conversationSignatureFactory,
}).then(function(tom) {
  console.log('Tom 登录');
}).catch(function(error) {
  // 如果 signatureFactory 抛出了异常,或者签名没有验证通过,会在这里被捕获
});

实际使用中遇到的问题

  1. 当action为create 时,不需要把action push到msg中

  2. 当邀请用户加入对话时,云函数收到的action为‘add’,此时直接使用add 加入到msg中生产的签名验证不通过,后经过使用签名测试工具发现对话签名类型只有三种:

    开启对话(create)、 加入对话(invite)、踢出对话(kick );

    所以当action为‘add'时应替换为'invite' push到msg中来生成签名

云函数实现签名算法:

AV.Cloud.useMasterKey();
let crypto = require('crypto');
console.log('接收参数', request.params);
let clientId = request.params.clientId.id === undefined ? request.params.clientId : request.params.clientId.id; // 当前用户ID 如果获取Client使用user对象 则这里的需要取clientId.id
let conversationId = request.params.conversationId;
let targetIds = request.params.targetIds; // 此次操作的目标用户
let action = request.params.action; // 此次行为的动作,可能的值为 create(创建对话)、add(加群和邀请)和 remove(踢出群)之一
let msg = ['您应用appId', clientId];
if (conversationId) {
    msg.push(conversationId);
}
// 如果该用户不允许创建对话可以在这里直接返回一个 no 即可
if (targetIds.length) {
    targetIds.sort();
    msg.push(targetIds.join(':'));
} else {
    msg.push('');
}
let ts = parseInt(new Date().getTime() / 1000);
let d = [];
for (let i=0; i<5; i++) {
    d.push(parseInt(Math.random()*10));
}
let nonce = d.join('');

msg.push(ts);
msg.push(nonce);
if(action) {
    if(action !== 'create') {
        if (action === 'add' || action ==='invite' ) {
             msg.push('invite'); // add 动作也要使用invite来验证签名
        }
        if (action === 'remove' || action ==='kick' ) {
             msg.push('kick');
        }
    }
}
msg = msg.join(':');
console.log('待检验的字符', msg);
let sig =crypto.createHmac('sha1', '你应用的masterKey').update(msg).digest('hex');
let res ={ 'nonce': nonce, 'timestamp': ts, 'signature': sig, 'msg': msg };
console.log(res);
return res;

测试签名

进入控制台在 消息 > 实时消息 > 用户,输入一个 clientId 进行查找, 找到后界面会显示 测试签名

监听事件

拥有监听事件的对象 IMClient, Conversation

on(event, listener, contextopt) → {this} 注册监听事件

给指定的 event 添加监听器,并将该监听器置于监听器列表的末位。该方法不会检查是否已经添加过该监听器。重复添加相同的 event 和 listener 会导致该事件和监听器被重复触发。

根据实际情况可以rn生命周期的不同阶段来注册监听事件

例如:

conversation.on(Event.MESSAGE, this.newMessage) // 监听新消息

newMessage = (message) => {
    console.log(message)
  }

off(event, listeneropt, contextopt, onceopt) → {this} 移除监听事件

移除 event 事件的监听器列表中的 listener。 一般在页面销毁前卸载所有监听事件

conversation.off(Event.MESSAGE, this.newMessage) // 卸载监听事件

注意:off 有两种情况

var callback = function() {};
conversation.on(Event.MESSAGE, callback);
conversation.off(Event.MESSAGE, callback); // off注册Event.MESSAG 事件的回调callback
conversation.off(Event.MESSAGE); // off掉所有的注册的 Event.MESSAG 事件回掉

once(event, listener, contextopt) → {this} 一次性的监听

为 event 事件添加一个一次性的监听器,该事件第一次触发之后就会被注销。

目前使用到的事件

客户端事件

  • DISCONNECT:与服务端连接断开,此时聊天服务不可用。
  • OFFLINE:网络不可用。
  • ONLINE:网络恢复。
  • SCHEDULE:计划在一段时间后尝试重连,此时聊天服务仍不可用。
  • RETRY:正在重连。
  • RECONNECT:与服务端连接恢复,此时聊天服务可用。
  • MESSAGE : 监听消息
  • UNREAD_MESSAGES_COUNT_UPDATE : 监听对话的未读消息数量

对话监听事件

  • MESSAGE : 监听消息
  • MEMBERS_JOINED : 有用户被添加至某个对话
  • MEMBERS_LEFT : 有成员被从某个对话中移除
  • KICKED : 当前用户被从对话中移除

React-Native 事件监听与回调

事件监听

用于解决子页面某些状态发生变化后,需要在返回父页面时,父页面能得到通知进行一些其他操作,如:刷新页面、更改某个状态等

参考资料:www.jianshu.com/p/847fbba8b…

首先父页面引入DeviceEventEmitter

import {
 DeviceEventEmitter,//引入监听事件
} from 'react-native';

然后在父页面的componentDidMount方法注册一个监听事件

//注册通知
  componentDidMount(){
        DeviceEventEmitter.addListener('ChangeUI',(dic)=>{
            //接收到详情页发送的通知,进行自己的操作如刷新页面,改变样式等
            console.log(dic)
       });
  }

子页面也要引入DeviceEventEmitter,然后在componentWillUnmount发送一个通知方法给父页面

DeviceEventEmitter.emit('ChangeUI', {color:'red',text:'通知'});

移除监听

componentWillUnmount(){
    // 移除监听 
    this.listener.remove();
}

事件回调

A界面在push到B界面的时候定义个回调函数

push = () =>{
    this.props.navigator.push({
        component:DetailsView,
        passProps:{
            callback:(msg)=>{ alert(msg) }
        }
    })
}

B界面在pop回A界面的时候调用该回调函数

pop = () =>{
    this.props.navigator.pop({
    })

    if(this.props.callback){
        this.props.callback('回调')
    }
}



原文地址:访问原文地址
快照地址: 访问文章快照