基于XMPP的Android即时通讯应用设计方案。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/limb99/article/details/40435899

这个文章想写了好久没有动笔。感觉都有点生疏了。

之前在一片文章里提过Android Xmpp做IM的事。做了几个月自己有了更深的了解。

Android IM应用,方案 :openfire + asmack

对于大多数IM应用,这个方案还是不错的。我之前对这个XMPP适用移动通信产生质疑(比如:通信效率低,网络穿透能力差)。不过现在我还是比较看好他的。

先说个成功案例吧--环信。环信就是基于XMPP协议开发,现在号称可以支持千万级的用户。当然,环信做了大量的私有化在XMPP的基础上。他们的Android端sdk也是在asmakc的基础上开发的。


一、服务器端的设计方案。

服务器在Openfire的基础上做插件开发。按客户端和服务器数据库数据交互的路径可分为三类(见下图):

1.通过自己写的插件直接操作数据库。

2.通过自己写的插件调用openfire的接口完成数据的交互。

3.直接通过openfire的接口完成数据的交互。


( 具体操作留坑待填)


客户端服务器交互方式

客户端获取数据的方式有两种,一种是推送,一种是拉取。在我们的应用设计中对应Message和IQ。

Message包的设计下面会讲到。

IQ指info/Query。是一问一答的机制,当发送一个set或者get类型的IQ到服务器,服务器必须返回一个result或者error类型的IQ给客户端。

使用http进行数据交互的时候,可以通过地址和参数来区分不同的请求。长连接的数据交互都是以包为单位,区分不同的请求是通过包信息识别的。对应xmpp的设计中就是IQ的命名空间和其拓展数据。按xmpp的规则自定义IQ的话略显复杂,所以我们采用了填充json的方式来扩展数据。比如我们通过IQ发送一条微博:

<iq from='zhangsan@example.com' type='set'>
    <data xmlns='com.example.test.weibo' action="add"/>
        {"content":"今天天气不错",
         "time":"2014-11-28 14:00:01",
         "images":["/image/test1.png",
                   "/image/test2.png",
                   "/image/test3.png"
                  ]
        }
    </data>
</iq>


IM软件要解决的几个基本问题

1.消息类

.不同类型的消息:图片、声音、文字等。离线消息

2.联系人(好友)

联系人的常规操作,添加、删除、分组

3.连接管理

包括数据包的监听,连接的创建、断开、重连

4.其他

数据推送,多终端数据同步


先说消息的解决方案。

XMPP的所有数据包都是xml格式的数据,并且有自己的拓展规则。但不得不说的是xml格式的数据传输效率比较低,因为存在大量标签,所以数据包里的有效数据比例降低。特别是在移动网络的情况下,这种格式的数据包非常的浪费流量。我们采用的包结构式xml+json。一来提高了数据传输效率,二来一下子从繁琐的xmpp拓展协议解脱出来,换成了喜闻乐见的json。例如:

文本类型的消息:

<message
    to='zhangsan@example.com'
    from='lisi@example.com/android1.0'
    type='chat'>
    <body>
        {"type": "text",
         "data": "今天天气不错"
        }
    </body>
</message>

图片类型的消息:

<message
    to='zhangsan@example.com'
    from='lisi@example.com/android1.0'
    type='chat'>
    <body>
    {"type":"image",
     "data":{"url":"/image/test.png",
             "thumbUrl":"/image/thumb/test.png",
             "width":150,
             "height":120
            }
    }
    </body>
</message>

        openfire已经完成了个人消息的离线处理。服务器会在你登陆后,将所有的离线消息推送给你,你在登陆后监听来自服务器的所有消息包即可。严格讲openfire现在的群聊应该叫做多人聊天室,openfire会自动记录群内的所有聊天内容并存储(数据库表ofmucconversationlog)。你需要做的是在每次退出登录时,记录一个时间,下次登录的时候 将聊天记录表中这个时间之后的所有消息推送给该用户(这里要注意,openfire的多人聊天记录,不会被立刻写入数据库,openfire会把记录写入缓存,待到达一定条数或者一定时间过后一起写入数据库。针对这个情况,我们现阶段用了个不好的方法,就是不使用缓存)。

        在xmpp的设计中好友关系分为3种:双向好友,单向好友,没有关系。在好友关系变更的时候会有两项数据会改变,一个是花名册(roster),一个是订阅关系,两者是同事变更的。花名册说白了就是你的好友列表,而订阅关系就是指是否接收对方的在线状态等(比如好友上线了你是否能知道)。

连接管理

        因为移动设备网络环境的不稳定性,连接的处理显的尤为重要。asmack中ConnectionListener定义了连接的各种事件,可以通过XMPPConnection的addConnectionListener方法添加连接监听。asmack也有个自动重连的类(ReconnectionManager),但是单纯的在配置中设置是无效的config.setReconnectionAllowed(true);这个类构造方法私有化了,可以通过反射机制去加载这个类使其生效Class.forName("org.jivesoftware.smack.ReconnectionManager");除此之外,长连接通常还设置心跳包,asmack也有对应的实现。如果有客户端发送ping包的话PingManager.getInstanceFor(conn).setPingInterval(20);(这里的单位是s...刚开始还以为是ms,困惑好久),添加ping的同时还可以添加ping失败的监听(registerPingFailedListener),来处理ping失败后的事项。尽管这些已经能保持连接的可靠性,但仍有问题的存在。在连接断开和知道连接断开这段时间内的Message包会丢失,这时候就要引入一个回执机制来确保发送的所有包都已到达,对应的xmpp协议XEP-0184。asmack中对应的实现类为DeliveryReceiptManager。


多终端、版本兼容问题

应用上线一段时间后,可能会有多个版本在流通,甚至是多平台多版本。比如Android1.5,Android2.0,IOS1.0等等。如果一开始设计没有很好地考虑这个问题,后期必将是个坑。xmpp在这方面已经有相应的解决方案。在用户登录的时候有个resource的参数,在这里我们可以传入当前应用的版本及平台信息。比如上面的例子,我们想要废弃Android2.0之前的版本,可以在登录的时候是否是Android平台版本号是否<2.0 。在多版本通信的时候如果数据结构不一致,高版本需要兼容低版本。从Message的from参数中可以看到消息来自哪个平台哪个版本的终端。


推送

对于IM软件来讲推送就变得非常简单了。其实就是发消息,根据不同需求发不同消息就可以了。项目中我把推送内容分为两类:一类是通知,服务器可以通过Message的from参数来控制消息发向哪个会话。一类是同步数据,目的就是为了保证服务器和客户端的数据一致性。

为了保证客户端和服务器数据的一致性通常有两种方案。

方案一,本地应用不保存数据,每次从服务器获取全量的数据。

方案二,本地保存数据,每次获取增量数据,保证与服务器的数据同步。

IM这种应用第二种方案最为适合。特别是移动应用,每次拉取全量的数据不仅浪费流量,而且应用也无法离线使用。

        这里说说我对这种增量数据同步的一些想法。为了保证数据同步的实时性,如果应用离线,数据同步操作在登陆成功到进入应用这段时间完成,如果应用在线,应用后台服务器应直接处理。关于这个数据同步还有一些问题没有完美的解决方案。像这种增量的数据操作,错误都会累积,如果上次的数据没有能正确处理,可能导致下次数据同步时出错。我想了想大概有这么两种可能。一是丢包,从服务器推送的同步数据丢失,导致客户端的数据并没有和服务器保持一致。针对这种情况,可以设计一个反馈机制。如果客户端收到数据包的话,给服务器一个再发一个数据包,告知服务器数据已收到。二是本地数据处理异常。由于一些未知的问题导致数据未能正确存储到本地。数据的不一致可能导致应用发生一些不可预知的错误,这时候最好是强制关闭应用,同时保持一个数据同步失败的状态到本地,下次启动的时候检查到上次同步失败的状态的时候,向服务器请求全量的数据并更新到本地数据库。

应用场景,比如自己所在的群有个成员被删除了,这个时候需要更新群成员列表。


应用的启动流程


如上图,应用启动要处理的事情包括:身份验证,数据库、监听等的初始化,离线数据处理、同步数据处理,进入应用。

登陆之前要先建立匿名连接,然后在进行身份验证。

身份验证成功后,要做相应的初始化操作。如监听的初始化,数据库的初始化,用户配置的初始化。监听初始化,是为了下一步监听服务器发来的数据包。(之所以初始化在登陆成功后进行,是因为在登陆成功前你不知道要使用哪个配置文件、哪个数据库,比如一个手机切换多个账号登陆)

在初始化完成,这时候客户端已经准备就绪,发送一个presence包通知服务器可以接收数据了,服务器开始向客户端推送数据,推送的数据包括离线消息,同步数据。处理这些数据(通常是写入数据库)。完成这些就可以进入应用了。



 ( 还有些东西没写完,有时间再更新)


展开阅读全文

没有更多推荐了,返回首页