早在一年之前,就曾负责一款IM移动通信类型的App开发,部分相关的功能都是仿照微信来进行,例如:聊天,通信录,朋友圈
等。由于,整个团队都没有IM方面的经验,我们只能不断地摸索前进,所以这篇文章也只适合初级IM开发者来参考之用。
对于开发一款App,即是成立一个先的项目组,对于前期的立项,需求确定,资源分配就直接跳过,那些是属于项目管理与产
品经理的范畴。我们就直接进入开发阶段。
1.首先确定需求,这里我们称之为功能,对于程序猿来说更贴近一点。
- 聊天 ,其中包括单聊,群聊。
- 通讯录,其中包括加好友,验证好友。
- 朋友圈,其中包括时间线,图片上传下载,及保存。
2.前期重要功能确定之后,就是系统框架的搭建。
- 服务器端: 消息系统,用户系统,朋友圈系统
- 客户端端: 消息管理框架,通讯录管理框架,朋友圈管理框架
3.确定开发次序,就是开放进度。
4.不断发现问题,不断改进。
以上四点是基本开发一个app的过程,以下就述说一下我们其中在开发过程中遇到的一些问题,和开发一款IM的摸索中的思路。
一,对于程序猿来说,确定需求就是确定功能。聊天功能,包括了一对一的聊天,多人的聊天。一对一的聊天,就是把消息推
送到另一个客户端的过程。群聊,就把消息推送多多个客户端。那么我们聊天功能中主要要考虑哪些问题呢?
- 消息推送
- 消息安全
- 消息离线
- 语言聊天
- 图片传输
- 消息掉包
- 消息格式
1.消息推送。 我们都是知道聊天系统中必定有消息推送系统,意义就是客户端与服务器端保持一个长连接,然后就服务器管理着
每一条这样的Socket长连接,当消息中心有该客户端的消息的时候,就会推送给客户端。这里推送模式首先就涉及了常见的三种方
式:1.定时轮询,主动拉取消息,2、socket推送消息,3、socket通知,主动拉取消息。
- 定时轮询,主动拉取消息 。 该模式更多适合广告方面的用途。例如我之前做的广告SDK中的banner,插屏等。如遇到需要更新广告信息的时候,就会定时重复去请求服务器,返回服务器所需显示的广告内容。又或者做单机支付的时候,由于三大运营商的审核标准都不一致,我们需要控制这些单机游戏在进行游戏道具购买的时候,用的是第三方平台支付还是短信支付时,都会用到定时轮询,主动拉取消息。由于,我们是即时通讯应用,如果是去主动拉取的话,那么就需要我在我们应用中开启轮询线程,每次都去问服务器是否又消息更新。这里就涉及到一个流量问题,和性能问题。性能问题:因为不断主动使用Http请求,会耗电。
- socket推送消息。 socket推送就是消息推送的主要实现途径。之前做游戏客户在线帮助功能的时候,就利用socket与服务器保持长连接,然后单线程地与服务器进行消息交流,为何是单线程?因为socket受到消息是阻塞的,这里涉及了socket通道的问题,随后再说。客户端登录成功后,与消息服务器保存一个socket长连接,当消息服务器收到指向该客户端的消息时候,再通过这个socket推送给这个客户端。 socket感觉就是为了即使通讯而出现的。但是由于移动网络的好难以稳定的,在网络差的时候,这种socket链接并没有Http那么强壮,很容易出现掉包
- socket通知,主动拉取消息。第一次接触这个模式,是看到一个分析与借鉴微信消息推送的一篇文章。主要通过socket与服务器维持通讯,这个通讯包含的协议比较特别,比心跳包外还加入一个功能Id,这个功能ID,主要是服务器返回,当服务器返回这个功能ID的时候,证明这个功能有消息更新,然后再去通过http接口,上去服务器拉取更新的内容。这种模式,完美的结合前两个者的风格。这种方式第一还是可以与服务器保持着一个长链接,第二消息推送的http响应比socket健壮得多,也就是掉包率会减少。因此,还是建议使用第三种。但是,这里会涉及到另一个问题。
以上是消息推送客户端与服务器端之间的通讯机制,消息推送中另一个重要的关键因素------消息协议。目前市面上,即百度上出
现的基本都是xmpp或者mqtt这两种协议。xxmp使用的是传统的xml文本形式的数据类型作为通讯协议,这种xml形式在前几年基本
都会应用到所有的IM类型应用,因为其开源方式和google官方使用的推送作为例子的广泛认知,基本上这种方式成了主流。包括早
其的米聊,前期也是使用xmpp的协议。最具代表的就是极光推送了。但这种形式的xml格式借用了太多的数据流量,在现在这个通
讯数据那么多的情况下,会耗费掉大量流量。所以我们放弃掉。
最后,我组的后端工程师找到了另一套开源的消息管理机制---------ActivityMQ。这个开源的框架的好处是支持自定义协议,它的
框架只会帮你处理一件事:保存你的消息队列,向你目的的socket发送消息,而且,它也是开源的~!因为其为我们提供了所需的消
息管理机制,我们服务器就不再为消息发送的机制来进行过多的开发,当然,开源的嘛,很多东西还是需要改为自己项目上需要
的。 而,真是这个服务器消息框架,促使我们暂时放弃了第三种消息推送机制。为何呢?请继续往下。 消息推送机制上,我们还是选择了第二种,只用socket来进行推送,这样服务器与客户端的逻辑就会变得鲜明一点,如下图:
上图可知,签收消息,是为了让服务器知道,客户端已经顺利接受了这条消息了,服务器可以在消息队列中把该条消息移除了。
没有这个签收机制的话,服务器的推送消息队列中会一直保留这条消息,会一直发送。为何要引入签收机制?因为在移动网络或者
任何网络情况先,并不能预知是否服务器的信息顺利呈现给了客户端用户的。其中的情况可能是,消息发送到了客户端了,但是可
能客户端在保存入数据库或者解释数据是出错,那么这条消息就会消失掉,我们俗称“被吃了”,并且在网络传输中,肯定会出现一
一定程度的掉包情况,所以这个签收机制就变得必不可少了。
而第三种的方式,服务器与客户端之间的逻辑大致如下:
由上图可知,第三种推送机制,类似于TCP协议一样,通过三次握手来确定一次会话,但正是这样的机制,其中包含多了两个步
骤,因此我们在未确定我们的App的将来规模以及时间的关系,我们果断采用省时省力的第二种,但我们深深地知道,当下一次大版
本来的时候,需要转换成第三种方式,原因上述已经说过了。
对于消息安全,就是防止一些非法黑客,对我们客户端与服务器端会话进行抓包。所以我们需要对我们的会话的数据进行加密,
看过微信的加密方式我们都知道,首先,微信是利用第三种消息推送机制,他们每一次握手的过程可以与服务器交换一个新的
SyncKey来进行信息加密,这种动态加密方式,强化了消息传输中的保密性,非常值得借鉴学习,而且这种机制对于消息状态同
步,增量,有序传输等需求都能满足。长连接通知/短连接获取与确认的方式是最应该被推荐了,但时间关系,我们就使用简单的
Token加密,非常危险的方式,一旦给人获取到Token的信息与加密方式,这种密文就没有任何意义。
消息离线,服务端收到要将客户端A发出的消息发送给客户端B的时候,B并不在线,就是说,B并没有与服务器有
socket链接。此时应当怎么处理消息呢?我们可以把这些未经过签收的消息,放置在一个新的消息队列中。当客户端
下次链接服务器的时候,就先调用这个未接收的消息队列中的信息,然后发送给客户端。所以我们利用userId和与
token标示一个消息,再查找这种未签收的消息就利用userid查找,然后校验token,验证用户是否在线,在线,发出。
语言聊天,可以参看我之前的两遍博客--- Speex的使用,Speex代码实现。
图片传输,聊天的过程中,会出现图片传输,但由于现在手机拍摄的图片都很大,基本是1M~3M之间的大小,如
果每次都传输1M以上的图片,不但其中会掉失的数据验证,而且也会占用客户端大量的上行带宽,所以这里对于图片
需要压缩,并且有可能需要分包传输,所以这部分放到下面和消息格式,即消息协议一起讨论。
消息格式,下次再码字。