(转) Android平台上关于IM的实践总结
前言
IM通信在互联网发展到现在已经是码农的世界里人尽皆知的技术,特别在当下移动互联网迅猛发展的时代这种技术的开发也更加火热,其中老牌的代表作就有QQ和MSN,和最近新崛起的微信,默默,易信,来往等眼花缭乱的各种应用都把IM技术应用其中。我是Android开发人员,写这篇文章主要原因也是因为我自己从事开发以来主要做过的几款APP都是包含着IM通信,在不断的摸爬滚打的解决问题的过程中,积累了一些经验记录便将其记录到博客中作为自己一个阶段性的总结,也可以分享其他需要的开发者,作为一种参考实践的方案,当然,我也并非神马大神级别的人物,如果博客中存在错误或者读者看完后觉得有疑问,欢迎在文章下方留下你的建议或问题作为评论。
从socket到XMPP协议
一个简单完成的IM通信流程如同下面的模型里面1-2-3-4的步骤所示,两个移动设备之间的收发消息都需要服务器进行中转并做消息的转发。
整个IM流程中,服务器接收消息发送方的请求,并检查接收方当前是否在线,如果在线的话则直接向接收方发送消息,如果不在线则作为离线消息存储到数据库中,等待接收方上线的时候再将消息进行发送。
IM作为一门网络通信的技术,必然涉及到通信协议的问题,而在Android平台上做IM开发,目前我所涉及到的协议有socket和XMPP两种类型,当然,这两者并不是一种并列的关系,从严格意义上来说socket是Java所有网络实现通信的基石,在Java上所有任何网络协议通信,最终都要依赖与socket来实现,在Android平台上使用XMPP协议通信,其最终底层同样还是依赖于socket实现,最典型的参考例子就是smack了,估计绝大多数小企业在做IM开发的时候,都会使用已经把XMPP协议封装好smack,以此节省开发时间,smack本身就是用Java语言开发,并采用了mina作为核心实现的框架(mina本身就是一个Java的NIO通信框架,但是很久不更新了,原先mina的作者写了一个新的NIO通信框架叫做netty,应该是做为mina的替代者)。作为socket和XMPP协议实现IM通信其主要的优缺点如下:
因此,在实际的IM开发中,到底是选用XMPP协议和使用socket来实现,可以根据开发应用场景和他们的优劣来决定,当然,也不仅仅局限于IM开发,服务器给客户端做消息推送也可以作为参考。
长连接和数据丢包
IM开发中除了基本的消息收发,以及协议使用方式的选择外,还需要解决所有IM通信技术所面临的两个共性问题。
其一,保持客户端和服务器间的长连接。这是由于业务场景所需要, IM通信实现的基础是需要时时刻刻保持客户端在服务器上的在线状态,这样才能保证发送方的消息能够及时到接收方,特别是微信的设计概念出来后,IM类型的客户端已经没有在线和离线状态的区别了,这是和QQ最大的区别 ,在这种情况下对客户端实时在线的要求就变得更高。那么作为客户端如何实时保持在线状态呢?由于Java网络通信的最底层都要依赖于socket来实现,而socket又分为UDP和TCP两种类型,从理论上来讲,我们使用TCP类型的socket来做实现通信即可保证长连接的实现,不过这仅仅是理论上而已,为什么单靠TCP不行咧,这跟我们真是的网络环境和操作系统都有一定的关系,预知为毛,请接着往下看。
首先,在上一面那种简单的IM通信图中,仅仅列出了IM通信的服务器和客户端,但是在实际的网络环境是非常复杂的(如下图所示,黄色的闪电图形代表一个连接),我们发送的消息报文是需要经过很多不同的运营商线路,在各个交换机和路由器间穿梭才能最终到达接收方的设备上,下图是一个简单的网络消息传递图,黄色的闪电线连起来就是TCP所保持的长连接,理论上这个链接是一直保持通畅的,所以客户端只需建立连接后就可以撒手不管。
但是从性能和耗费资源的角度来看,使用socket是非常耗费资源的,特别是保持长连接的TCP类型socket,所以在实际的运营商设备上,这个长连接保持一小段时间后就会被断开,这样做也是运营商为了节省和充分利用设备的资源,而中间有一环的链接断开之后,双方就再也无法保持长连接且进行通信,因为如果仅仅依赖于TCP的长连接,此时服务器和客户端是并不知道链接已经被断开的,所以就会造成下面将要说到的数据丢包问题。除了运营商自己会将设备的长连接断开外,还有包括其他原因也将导致链接断开,例如,运营商的设别断电或故障等、移动设别接收端本身设备断点掉线等诸多复杂的因素存在导致直接依赖于TCP的长连接方案不可行,其次例如Android操作系统本身也是会对TCP的长连接做处理,诸如之前所说的,TCP的socket长连接是非常耗费资源的,这种资源相对于移动设备来说更加宝贵,所以在一定时间的TCP长连接闲置后,系统也会自动清除设备上的长连接,以此节省移动设别上的能耗。为了彻底解决长连接的问题,IM技术实际上引入了心跳包的方案,说起来这种实现方案也并不复杂,就是客户端每个一会儿就要向服务器发送一个请求报文(这个报文可以是空的,跟服务器那边约定好),目的是为了告诉服务器“我是在线的”,而服务器在接收心跳请求报文之后同样需要反馈一个消息告诉客户端“我知道了”,通过应用层上的不断进行消息收发来维持长连接的存在,当客户端没有一直没有收到服务器的反馈时默认已经掉线,进行相关的业务逻辑处理后将再重连服务器,同样服务器没有收到客户端发送来的心跳包时,可以默认客户端已经掉线,将服务器上的通信socket进行关闭回收资源。
其二,客户端发送数据的丢包问题,在第一个原因中降到运营商和各种各样的网络情况都会导致发送数据的丢包,陷入以下的这种场景A和B之间同样经历1-2-3-4的一个消息收发流程,但是不幸的是由于各种原因,B返回的消息在IM服务器转发给A的时候丢失了,此时A并不知道B回复了他消息,而服务器也不知道A没有收到B的回复,这种情况下所带给用户的体验是及其差劲的。
那么要如何应对这种丢包问题呢?目前想到的一种简单粗暴的可行方案就服务器每次向客户端发送一条消息都要为这条消息做一个id的标记,客户端收到此消息后需要返回一个回执给服务器,作为标记当前客户端已经收到消息的证明,如果服务器发送出消息而迟迟没有收到服务器的回执,就把这些消息作为离线消息存放到数据库中,等到客户端重现上线的时候再将离线消息推送给客户端。通过这种方式就可以很好的解决了消息包发送途中丢包而客户端和服务器都不知情的问题。体验一下子就彪上去了,咔咔咔。
优化性能
由于IM通信的实时要求性比较高,伴随着长连接所耗费的资源也相对比较多,所以在移动平台上的IM通信客户端想要性能和体验上得去,性能优化是必不可少的,对此为Android平台上IM优化做一些小小的总结,当然不一定很全面,如果你有更好的建议欢迎在评论里面做补充哈。
小结
以上的内容仅仅时平时开发过程中积累的一部分知识和经验的总结,希望他能帮助到其他在寻找资料开发者,同时,也感谢很多开源项目的贡献者以及热爱分享的博客撰写者,因为有他们的贡献和分享才有这篇文章的诞生,才有我在开发过程中的进步。本文的word文档也会同步到我的github仓库上,欢迎拍砖,欢迎转载,但请注明出处,请勿随意用于商业用途。
(转) Android平台上关于IM的实践总结的更多相关文章
- Android平台上最好的几款免费的代码编辑器
使用正确的开发工具能够快速有效地完成源代码的编写和测试,使编程事半功倍.在网络信息高速发展的今天,移动设备的方便快捷已经深入人心,越来越多的程序员会选择在任何感觉舒适的地方使用移动设备查看或者编辑源代 ...
- 随笔之Android平台上的进程调度探讨
http://blog.csdn.net/innost/article/details/6940136 随笔之Android平台上的进程调度探讨 一由来 最近在翻阅MediaProvider的时候,突 ...
- Qt在Android平台上实现html转PDF的功能
Qt for Android Qt for Android enables you to run Qt 5 applications Android devices. All Qt modules ( ...
- 涂鸦基于OAuth2在开发者平台上的探索与实践
前言 开发授权(OAuth2)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资料(如照片.视频.联系人列表),而无需将用户名和密码提供给第三方应用. OAuth2允许用户提供一 ...
- OpenCV在Android平台上的应用
今年8月份, OpenCV 2.3.1发布了. 虽然从2.2开始, OpenCV就号称支持Android平台, 但真正能让OpenCV在Android上运行起来还是在2.3.1版本上. 在这个版本上, ...
- [原]详解如何将cocos2dx项目编译到Android平台上的(方式一:Cywin+NDK)
链接地址:http://m.blog.csdn.net/blog/yhc13429826359/29357815 2014-6-8阅读578 评论0 前言:cocos2dx作为一个开源的移动2D游戏框 ...
- unity3D开发的程序发布到Android平台上进行运行测试的详细步骤
第一步 下载安装JDK 和SDK 1.需要配置java环境.点击链接进入ava的配置的方法:http://www.cnblogs.com/Study088/p/7496158.html 2.下载 ...
- 如何在Android平台上使用USB Audio设备
http://blog.csdn.net/kevinx_xu/article/details/12951131 需求:USB Headset插上去后,声音要从本地CODEC切换到USB Headset ...
- 移动端开发:iOS与Android平台上问题列表
要CSS伪类 :active 生效,只需要给 document 绑定 touchstart 或 touchend 事件 <style> a { color: #000; } a:activ ...
随机推荐
- 开始Swift之旅 - HelloWorld
//: Playground - noun: a place where people can play import UIKit var str = "Hello, playground& ...
- Hibernate中session的产生的方式
* session的产生的方式 * 1. sessionFactory.openSession 每次都会新创建一个session,只要新创建一个session,hiber ...
- [转载]__type_traits
在STL中为了提供通用的操作而又不损失效率,我们用到了一种特殊的技巧,叫traits编程技巧.具体的来说,traits就是 通过定义一些结构体或类,并利用模板类特化和偏特化的能力,给类型赋予一些特性, ...
- 66 Plus One(大数+1Easy)
题目意思:vector<int> v存数 eg.123 则v[0]=1,v[1]=2,v[2]=3,加1后返回一个vector 思路:先判断新的vector长度是否需要加1,然后从v ...
- 去掉所有的html标签
去掉所有的HTML标签:$text=preg_replace('/<[^>]+>/','',$text); 去掉<img>标签:$text=preg_replace('/ ...
- apache静态文件配置
开发环境配置 需要下面几个步骤 1. 在app目录下创建static目录,将静态文件和相关文件夹放到此目录下,如your_app/static/img等 2. 确保settings.py中的INSTA ...
- C程序设计语言练习题1-23
练习1-23 编写一个删除C语言程序中所有的注释语句.要正确处理带引号的字符串与字符常量.在C语言中,注释不能嵌套. 代码如下: #include <stdio.h> // 包含标准库的信 ...
- c程序代码的内存布局(学好C的基础)
一个程序本质上都是由 BSS 段.data段.text段三个组成的.这样的概念在当前的计算机程序设计中是很重要的一个基本概念,而且在嵌入式系统的设计中也非常重要,牵涉到嵌入式系统运行时的内存大小分配, ...
- Lucene查询条数限制
运用Lucene进行索引,在查询的时候是有条数限制的 public virtual TopFieldDocs Search(Query query, Filter filter, int n, Sor ...
- Scut:从PackageReader分析客户端协议规则
看第一个解析API: private void ParseData(byte[] data) { var paramBytes = SplitBuffer(data); RawParam = _enc ...