Miryou 2019-06-27
本文基于 IM Andriod 开发的各种常见问题,结合网易云信即时通讯技术的实践,对 IM 开发做一个全面的总结。
安全性是 IM 软件的另一个硬需求。消息传递时如果通信数据如果被第三方截取,要能保证别人不能获取到真实内容。安全连接的过程可以参考 HTTPS 的方式,由服务器将证书下发给客户端,客户端产生一个对称的密钥,并通过服务器证书加密后交给服务器,之后的通信就全部使用这个对称的密钥来加密。当然,这里有两点需要和 HTTPS 有所区别,第一是证书的获取方式,HTTPS 中是由专门机构去验证证书合法性的,IM 的客户端肯定不会这么去做,为了防止获取证书的过程被人截获,然后篡改证书,可行的方式是直接在客户端安装包中直接把证书打进去,该证书可以随着客户端软件升级一起升级,也可以在加密连接之后通过协议升级。第二个问题是对称加密算法的选择,因为密钥的生命周期是跟随一次连接的,时间并不长,而移动 App 对于电量消耗非常敏感,因此加密算法应尽量选择较为简单的类型,例如 RC4。
心跳可以分为 TCP 的协议层心跳和 App 的应用层心跳。一般我们都使用应用层心跳,一来便于服务器扩展(比如哪天我们可以换成 UDP 来传),二则是可以更灵活控制心跳间隔。
心跳协议仅仅是用来连接保活,其内容应当尽量精简,除了包头中必要的部分,包体的可选包头都不存在。
对于不同的网络环境,心跳可以采用不同的时间间隔。在不同网络环境下,间隔的选择可以参考微信智能心跳方案。
客户端掉线的原因无非两种,客户端网络挂了,服务器挂了。客户端网络挂了也分两种,一种是本机就能感知到的网络连接断开,另一种是本机网络是好的,但互联网连接是不同的,对应到 Android API上,就是 NetworkInfo 的 isAvailable 和 isConnected。当然这个地方的 isConnected 不一定可靠,因为它是靠连制定服务器来确定的,那个服务器谁知道有没有问题。
掉线后,根据不同的状态需要选择不同的重连间隔。如果是本地网络出错,并不需要定时去重连,这时只需要监听网络状态,等到网络恢复后重连即可。如果网络变化非常频繁,特别是 App 处在后台运行时,对于重连也可以加上一定的频率控制,在保证一定消息实时性的同时,避免造成过多的电量消耗。
而如果掉线是因为本机网络连不通互联网,或者是服务器挂了,重连间隔的选择就非常重要了。
首先,如果程序是在前台,用户正在使用我们的 App,重连间隔应更加频繁,使得用户反馈更加及时,如果程序处于后台运行,则为了省电,可以适当延长重连间隔。
其次,随着重连次数的增加,说明服务器短时间内恢复的可能性逐渐降低,重连间隔也应随之延长(倍数增长)。但应该设置一个最大的重连间隔,当到达最大间隔时,不再增加。
第三,重连间隔的增加不应当是固定的,而应该增加一个随机退避策略。以免如果是服务器宕机造成掉线,所有客户端的重连时间点都是一样的,当服务器恢复后,同一个时间点所有客户端同时连接服务器,造成服务器不堪拥堵,再次宕机。活生生的例子请参考环信去年的宕机时间。
总结起来,重连间隔可表述如下:
IM 系统中另一个重头戏是多媒体数据。由于移动网络比较慢,流量又贵,在移动端针对这些问题必须要做一些处理。在上传时,尽量减少上传时间,在下载时,能让用户尽快看到内容。同时,尽量节省流量,减少不必要的流量消耗。
文本消息因为比较小,可以直接通过长连接传输。但对于多媒体文件,通过长连接来传输则不合适,长连接服务器不会对大文件传输做针对性优化,大量的多媒体文件数据会直接抢占其他信令消息和文本消息的贷款资源。因此,多媒体消息会通过另外的通道,到专门的文件服务器存取。
在下载时,对于不同的网络环境,可以采用不同的预取策略。在 WiFi 环境下,由于无需考虑流量问题,在收到消息后,我们就能立即把包含的多媒体文件下载下来。而在移动网络中,则应当等到用户真正看到该多媒体消息时,才去下载。
上传时,现在手机摄像头的像素动辄上千万,一张图片随随便便就好几 M,然而,通过 IM 软件传输的图片,通常对于质量要求并不会太高,如果我们直接将好几 M 的图片直接上传,往往费力又不讨好。在上传之前,将图片像素降低,并进行压缩,可以明显的减少上传转菊花的时间,减少用户流量消耗。如果用户确实要求图片质量,则提供一个原图选项。
如果是使用 http 上传,大文件会被分成多个数据块上传,前一个数据块传输成功后,再传输下一个。断线重传时,也是以数据块作为最小重传单元。针对不同的网络类型,数据块大小不同。在较好网络下(wifi/4g/3g),数据块可以比较大,这样可以减少交互时间,加快传输熟读,而在弱网环境,数据块应当设置的比较小,以降低传输失败概率,减少重传流量消耗。
使用 http 上传的另一个优化技术是使用 pipeline。在不使用 pipeline 时,上传一个数据块需要等到前一个数据块传输成功才行,数据通道是单工的。使用 pipeline 则可以将数据通道变为双工的,一个数据块传输完成后,不必等到回包,就能直接上传下一个数据包,能节省一次数据回包延时。
下载时,在消息展示区显示的通常只是一个很小的图片,这时候只需要下载对应大小的缩略图即可,无需下载原图。甚至,这里可以将比缩略图更小的图片二进制数据直接放到消息体中下发,并展示给用户一个高斯模糊后的效果图,在保证最低可用的情况下,减少用户等待时间,提高用户体验。
对于不同的网络环境,采取不同质量的语音编码算法,在较好网络时,使用高质量语音,而在弱网环境下,则使用较低质量,优先保证可用性。
为了减少用户等待时间,还可以采取边录边传的策略。由于录音时间比较长,在录制的过程中,我们就可以将录好的部分先传到服务器,等到录音完成,只需要上传最后一个数据包,并告知服务器录音完成即可,基本上可以做到录完即传完,无需等待。
语音消息没有缩略图,因此语音下载基本就只能实打实的将原始文件下载下来。
以上就是网易云信对于 Android 即时通讯开发的小结,欢迎各位积极提问,共同探讨。
随着即时通讯以及音频处理和压缩技术的不断发展,效果更好、适用范围更广、性能更高的算法和新的技术必将不断涌现,如果你有好的技术或者分享,欢迎关注网易云信官方博客和 GitHub:
关注更多技术干货内容:网易云信博客
欢迎关注网易云信 GitHub
欢迎关注网易云信官网