注册
IM

Android移动端IM开发之应用层实现TCP长连接多路复用



这里只是提供一个长连接多路复用的实现思路


什么是长连接多路复用



  1. 从字面意思看就是一台设备只有一条长连接连向服务器,其他集成这个IM SDK的app都会共享这条长连接,TCP长连接的维护是比较耗资源的,而且也会增加耗电,所以实现长连接共享就表示在一定程度上降低了耗电也节省了内存
  2. java里面还有一个IO多路复用,这个用在服务端的比较多,用来解决并发问题,服务器的IM框架netty也用到了IO多路复用,原先是一个客户端对应一个IO,服务器要为每个客户端单独开一个线程来处理客户端的消息请求,但是使用了IO多路复用后,可能多个客户端共享一条IO链路,节省了资源,不必为每个客户端来单独开一个线程处理,具体服务端怎么实现我也没去深究,想多了解就上网找找资料或者看下netty的源码实现

IM的长连接多路复用的实现


IM的长连接多路复用的实现我觉得分为两部分,一部分是服务端的实现,一部分是客户端的实现
1. 一般一台手机设备会对应一个唯一的设备id,这个设备id对于服务器来说是绝对唯一的,是标识设备唯一性的一个标志,所以IM的接入服务器可以根据设备id来检测并控制每台设备连到服务器的长连接数量,接入服务器可以让同一台设备如果有两条不同的tcp连接连进来,那么可以断开其中一条tcp而只保持唯一一条tcp连接,这是从服务端保证单台设备TCP连接的唯一性
2. android客户端会集成响应的IM SDK,一般tcp长连接由service来维护,至于进程保活其实也就是service保活,因为push进程一般是和app的进程分开的;那么要实现在android手机设备上tcp连接的唯一性其实也就是保证维护tcp连接的service的唯一性,在android sdk中是可以有办法来判断service是否在运行的:


public static boolean isServiceRunning(Context mContext, String clsName) {
boolean isRunning = false;
ActivityManager myAM = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> myList = myAM.getRunningServices(Integer.MAX_VALUE);
if (myList == null || myList.size() <= 0) {
return false;
}
for (int i = 0; i < myList.size(); i++) {
String mName = myList.get(i).service.getClassName().toString();
if (mName.equals(clsName)) {
isRunning = true;
break;
}
}
return isRunning;
}

其实思路很简单,检测到这个service已经运行了,那么就不start service了,而只要bind service来获取binder对象就足够了,然后可以通过aidl来实现跨进程通信了,但是这里有两个概念,就是必须指定宿主app和寄生app,宿主app就是负责维护唯一的tcp长连接,寄生app的收发消息就通过宿主app来路由,这样就做到了tcp长连接共享,所以在检测到宿主app维护长连接的service没有启动的时候寄生app就可以start service并且bind service,但是注意了,这里不能通过简单的action来启动service,要指定正确的ComponentName,或者把宿主app的context传进来:


Intent serviceIntent = new Intent();
serviceIntent.setComponent(new ComponentName(componentPckName, ConnectionService.class.getName()));
serviceIntent.setPackage(context.getPackageName());
serviceIntent.setAction(ConnectionService.ACTION);

如上componentPckName必须是宿主app的package name或者宿主app的context,那么在寄生app里面启动service才能正确启动宿主app的service,这就相当于唤醒了宿主app了,如果寄生app只是简单的通过action来start service那么它将会为自己启动一个service,而不会去启动宿主app的service,到这里寄生app其实已经开始公用宿主app的service,我已经实现了,aidl调用正常,这样也省却了im连接成功后的初始化操作,只需要在长连接第一次连接成功的时候初始化以下,后面其他的app直接bind service获取binder来直接调用aidl接口就行了。这里有一个可以优化的地方,多进程之间的通信我采用的是aidl来实现,aidl代码冗余,而且传输数据比较慢,比什么慢?我觉得比tcp长连接慢,也就是说可以用本地的tcp长连接来实现进程间的通信,把宿主app当作一个服务器,寄生app相当于客户端,然后每个寄生app都建立一条和宿主app的长连接来实现数据传输,但是要使用长连接来传输数据就必须要制定数据编码格式,因为长连接的数据传输的流传输,有粘包问题,所以要对数据进行编码校验然后解码,而且要维护这条长连接也要有一些工作量,例如长连接断线重连等,所以有利有弊,目前我暂时选择的是aidl来实现进程之间的通信,有可能以后会改成tcp长连接的通信,tcp长连接可以实现宿主app和寄生app互相监听,让service的存活率更高,我同时也不得不承认,这很流氓。。。

0 个评论

要回复文章请先登录注册