基于XMPP的通信方案

XMPP协议(Extensible Messaging and PresenceProtocol,可扩展消息处理现场协议)是一种基于XML的协议,目的是为了解决及时通信标准而提出来的,最早是在Jabber上实现的。Android_IM 中sdk多采用ASmack实现,第三方IM通信SDK环信号称可以支持千万级用户,就是采用ASmack作为基础实现的。

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

服务器在Openfire的基础上做插件开发。按客户端和服务器数据库数据交互的路径可分为三类(见下图):
1.通过自己写的插件直接操作数据库。
2.通过自己写的插件调用openfire的接口完成数据的交互。
3.直接通过openfire的接口完成数据的交互。

image

二、客户端服务器交互方式

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

IQ指info/Query。是一问一答的机制,当发送一个set或者get类型的IQ到服务器,服务器必须返回一个result或者error类型的IQ给客户端。
使用http进行数据交互的时候,可以通过地址和参数来区分不同的请求。长连接的数据交互都是以包为单位,区分不同的请求是通过包信息识别的。对应xmpp的设计中就是IQ的命名空间和其拓展数据。按xmpp的规则自定义IQ的话略显复杂,所以我们采用了填充json的方式来扩展数据。比如我们通过IQ发送一条微博:

[html] view plain copy print?<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。例如:
文本类型的消息:

[html] view plain copy print?<message  
    to='zhangsan@example.com'  
    from='lisi@example.com/android1.0'  
    type='chat'>  
    <body>  
        {"type": "text",  
         "data": "今天天气不错"  
        }  
    </body>  
</message>  

图片类型的消息:

[html] view plain copy print?<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这种应用第二种方案最为适合。特别是移动应用,每次拉取全量的数据不仅浪费流量,而且应用也无法离线使用。

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

应用的启动流程

image

  如上图,应用启动要处理的事情包括:身份验证,数据库、监听等的初始化,离线数据处理、同步数据处理,进入应用。
登陆之前要先建立匿名连接,然后在进行身份验证。
身份验证成功后,要做相应的初始化操作。如监听的初始化,数据库的初始化,用户配置的初始化。监听初始化,是为了下一步监听服务器发来的数据包。(之所以初始化在登陆成功后进行,是因为在登陆成功前你不知道要使用哪个配置文件、哪个数据库,比如一个手机切换多个账号登陆)
在初始化完成,这时候客户端已经准备就绪,发送一个presence包通知服务器可以接收数据了,服务器开始向客户端推送数据,推送的数据包括离线消息,同步数据。处理这些数据(通常是写入数据库)。完成这些就可以进入应用了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容