移动IM开发那些事:技术选型和常见问题

1.jpg

最近在做一个iOS IM SDK,在内部试用的阶段,不断有兄弟部门或者合作伙伴过来问各种技术细节,所以统一写一篇文章记录,统一介绍下一个IM APP的方方面面,包括技术选型(包括通讯方式,网络连接方式,协议选择)和常见问题。

通讯方式选择

IM通讯方式无非两种选择:设备直连(P2P)和通过服务器中转。

P2P

P2P多见于局域网内聊天工具,典型的应用有:飞鸽传书,天网Maze(你懂的)等。这类软件在启动后一般做两件事情

进行UDP广播:发送自己信息和接受同局域网内其他端信息

开启TCP监听:等待其他端进行连接

详细的流程可以参考飞鸽传书源码。但是这种方式在有种种限制和不便:一方面它只适合在线的点对点消息传输,而对离线,群组等业务支持不够。另一方面由于 NAT 的存在,使得不同局域网内机器互联的难度大大上升,在某些网络类型(对称NAT)下无法建立连接。

服务器中转

几乎所有互联网IM产品都采用服务器中转这种方式进行消息传输,相对于P2P的方式,它有如下的优点:

  • 能够支持更多P2P无法支持或支持不好的业务,如离线消息,群组,聊天室服务

  • 方便业务逻辑的拓展和新旧版本的兼容

当然它也有自己的问题:服务器架构复杂,并发要求高。

网络连接方式

IM主流网络连接方式有两种:

  • 基于TCP的长连接

  • 基于HTTP短连接PULL的方式

后者常见于WEB IM系统(当然现在很多WEB IM都是基于WebSocket实现),它的优点是实现简单,方便开发上手,问题是流量大,服务器负载较大,消息及时性无法很好地保证,对大规模的用户量支持不够,比较适合小型的IM系统,如一个小网站的客户系统。

基于TCP长连接则够更好地支持大批量用户,问题是客户端和服务器的实现比较复杂。当然也还有一些变种,如下行使用MQTT进行服务器通知/消息的下发,上行使用HTTP短连接进行指令和消息的上传。这种方式能够保证下行消息/指令的及时性,但是在弱网络下上行慢的问题还是比较严重。早期的来往就是基于这种方式。

协议选择

IM协议选择原则一般是:易于拓展,方便覆盖各种业务逻辑,同时又比较节约流量。后一点的需求在移动端IM上尤其重要。

常见的协议有:

  • XMPP

  • SIP

  • MQTT

  • 私有协议

XMPP协议的优点在于:协议开源,可拓展性强,在各个端(包括服务器)有各种语言的实现,开发者接入方便。但是缺点也是不少:XML表现力弱,有太多冗余信息,流量大,实际使用时有大量天坑。

SIP协议多用于VOIP相关的模块,是一种文本协议,由于我并没有实际用过,所以不做评论,但从它是文本协议这一点几乎可以断定它的流量不会小。

MQTT的优点是协议简单,流量少,但是它并不是一个专门为IM设计的协议,多使用于推送。

而市面上几乎所有主流IM APP都是是使用私有协议,一个被良好设计的私有协议一般有如下优点:高效,节约流量(一般使用二进制协议),安全性高,难以破解。缺点则是在开发初期没有现有样列可以参考,对于设计者的要求比较高。

一个好的协议需要满足如下条件:高效,简洁,可读性好,节约流量,易于拓展,同时又能够匹配当前团队的技术堆栈。基于如上原则,我们可以推出: 如果团队小,团队技术在IM上积累不够可以考虑使用XMPP或者MQTT+HTTP短连接的实现。反之可以考虑自己设计和实现私有协议。

私有协议的设计

序列化选择

移动互联网相对于有线网络最大特点是:带宽低,延迟高,丢包率高和稳定性差,流量费用高。所以在私有协议的序列化上一般使用二进制协议,而不是文本协议。常见的二进制序列化库有protobuf和MessagePack,当然你也可以自己实现自己的二进制协议序列化和反序列的过程,比如蘑菇街的TeamTalk。但是前面二者无论是可拓展性还是可读性都完爆TeamTalk(TeamTalk连Variant都不支持,一个int传输时固定占用4个字节),所以大部分情况下还是不推荐自己去实现二进制协议的序列化和反序列化过程。

协议格式设计

基于TCP的应用层协议一般都分为包头和包体(如HTTP),IM协议也不例外。包头一般用于表示每个请求/反馈的公共部分,如包长,请求类型,返回码等。 而包头则填充不同请求/反馈对应的信息。

一个最简单的包头可以定义为

1
2
3
4
5
6
7
struct PackHeader
{
    int32_t     length_;    //包长度
    int32_t     serial_;    //包序列号
    int32_t     command_;   //包请求类型
    int32_t     code_;      //返回码
};

以心跳包为例,假设当前的serial为1,心跳包的command为10,那么使用MessagePack做序列化时:length=4,serial=1,command=10,code=0,每个字段各占一个字节,包体为空,仅需要4个字节。

当然这是最简单的一个例子,面对真正的业务逻辑时,包体里面会需要塞入更多地信息,这个需要开发根据自己的业务逻辑总结公共部分,如为了兼容加入的协议版本号,为了负载均衡加入的模块id等。

其他问题

上面就是一个IM系统大致的选型过程:通讯方式,连接方式,协议选择,协议设计。但是实际开发过程中还有大量的问题需要处理。

协议加密

为了保证协议不容易被破解,市面上几乎所有主流IM都会对协议进行加密传输。常见的流程和HTTPS加密相似:建立连接后,客户端和服务器进行进行协商,最终客户端获得一个当前Sessino的秘钥,后续的数据传输都通过这个秘钥进行加解密。一般出于效率的考虑都会采用流式加密,如RC4。而前期协商过程则推荐使用AES等非对称加密以增加破解难度。

快速连接(登录)

对iOS APP而言,因为没有真后台的存在,APP每次启动基本都需要一次重连登录(短时间内切换除外),所以如何快速重连重登就非常重要。常见的优化思路如下:

  • 本地缓存服务器IP并定期刷新。移动网络调优可以参考《iOS移动网络调优那些事》。

  • 合并部分请求。如加密和登录操作可以合并为同一个操作,这样就可以减少一次不必要的网络请求来回的时间。

  • 简化登录后的同步请求,部分同步请求可以推迟到UI操作时进行,如群成员信息刷新。

连接保持

一般APP实现连接保持的方式无非是采用应用层的心跳,通过心跳包的超时和其他条件(网络切换)来执行重连操作。那么问题来了:为什么要使用应用层心跳和如何设计应用层心跳。

众所周知TCP协议是有KEEPALIVE这个设置选项,设置为KEEPALIVE后,客户端每隔N秒(默认是7200s)会向服务器发送一个发送心跳包。但实际操作中我们更多的时是使用应用层心跳。原因如下:

  • KEEPALIVE对服务器负载压力比较大(服务器大大是这么说的...)

  • socks代理对KEEPALIVE并不支持

  • 部分复杂情况下KEEPALIVE会失效,如路由器挂掉,网络直接被拔除

移动端在实际操作时为了节约流量和电量一般会在心跳包上做一些小优化

  • 尽量精简心跳包,保证一个心跳包大小在10字节之内

  • 心跳包只在空闲时发送 (收到最后一个数据包n秒内再也没有收到包则进行一次心跳)

  • 根据APP前后台状态调整心跳包间隔 (主要是安卓)

消息可达

在移动网络下,丢包,网络重连等情况非常之多,为了保证消息的可达,一般需要做消息回执和重发机制。参考易信,每条消息会最多会有3次重发,超时时间为15秒,同时在发送之前会检测当前连接状态,如果当前连接并没有正确建立,缓存消息切定时检查(每隔2秒检查一次,检查15次)。所以一条消息在最差的情况下会有2分多的重试时间,以保证消息的可达。

因为重发的存在,接受端偶尔会收到重复的两条消息,这种情况下就需要接收端进行去重。一般的做法是每条消息都有自己唯一的message id(一般是uuid)。

文件上传优化

IM消息(包括SNS模块)内包含大量的文件上传的需求,如何优化文件的上传就成了一个比较大的主题。常见有下面这些优化思路:

  • 将上传流程提前:音频提供边录边传。朋友圈的图片进行预上传,选择图片后用户一般会进行文本输入,在这段时间内后台就可以默默将选好的图片进行上传。

  • 提供闪电上传的方式:服务器根据MD5进行文件去重。

  • 优化和上传服务器的连接(参考快速连接),提供连接重用的功能。

  • 文件分块上传:因为移动网络丢包严重,将文件分块上传可以使得一个分组包含合理数量的TCP包,使得重试概率下降,重试代价变小,更容易上传到服务器。

  • 在分包的前提下支持上传的pipeline,避免不必要的网络等待时间。

  • 支持断点续传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247895.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

webstrom打开通过顶部浏览器打开网页,被跳转到默认主页

重新开始工作啦,希望以后认真一点,并把遇到的问题都记录下来,虽然是小小白,但能无意间帮助到别人就更开心了呀 通过webstrom打开本地的文件时,发现跳转到了默认主页上,吐槽下,有些主页真的超级流…

mockjs(接口服务代理)

mock官网:http://mockjs.com/ 一、搭建一个练习项目 1.利用vue的项目脚手架进行搭建 命令: vue create mock-demo 截图: 2.安装相关的依赖 命令: #使用 axios 发送 ajax npm install axios --save #使用 mock.js 产生随机数据…

MD5算法原理

MD5(单向散列算法) 的全称是Message-Digest Algorithm 5(信息-摘要算法),经MD2、MD3和MD4发展而来。MD5算法的使用不需要支付任何版权费用。MD5功能:输入任意长度的信息,经过处理,输…

函数-函数进阶-装饰器流程分析

老王:算了,估计你也想不出来。。。学过嵌套函数没有? 你:yes,然后呢? 老王:想实现一开始你写的america login(america)不触发你函数的执行,只需要在这个login里面再定义一层函数&am…

制作手写签名

<!DOCTYPE html> <!-- saved from url(0056)http://hao2013.cn/canvas-special-master/brush/index.html --> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><title>签名板(支持移动…

python第五次作业——陈灵院

习题1&#xff1a;读入文件pmi_days.csv&#xff0c;完成以下操作&#xff1a;1.统计质量等级对应的天数&#xff0c;例如&#xff1a;优&#xff1a;5天良&#xff1a;3天中度污染&#xff1a;2天2.找出PMI2.5的最大值和最小值&#xff0c;分别指出是哪一天。 import csv impo…

iOS 二叉树相关算法实现

什么是二叉树&#xff1f; 在计算机科学中&#xff0c;二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”和“右子树”&#xff0c;左子树和右子树同时也是二叉树。二叉树的子树有左右之分&#xff0c;并且次序不能任意颠倒。二叉树是递归定义的&#xff0c;所…

vux 组件库首次使用安装

1、首先通过脚手架新建一个项目&#xff0c;过程略。 创建完项目后&#xff0c;在项目里安装vux&#xff0c; 通过命令 npm install vux --save 安装 2、安装vux-loader&#xff0c; 通过命令 npm install vux-loader --save-dev 安装&#xff08;vux文档没说明&#xff09; 3、…

@Component 和 @Bean 的区别

Spring帮助我们管理Bean分为两个部分&#xff0c;一个是注册Bean&#xff0c;一个装配Bean。完成这两个动作有三种方式&#xff0c;一种是使用自动配置的方式、一种是使用JavaConfig的方式&#xff0c;一种就是使用XML配置的方式。 Compent 作用就相当于 XML配置 Component pub…

js动态验证码获取

<!DOCTYPE html> <html lang"cn"> <head><meta charset"UTF-8"><title>短信验证码</title> </head> <body> <input type"number" id"tel" value"13303861063"> <…

Base64 算法原理,以及编码、解码【加密、解密】 介绍

Base64编码&#xff0c;是我们程序开发中经常使用到的编码方法。它是一种基于用64个可打印字符来表示二进制数据的表示方法。它通常用作存储、传输一些二进制数据编码方法&#xff01;也是MIME&#xff08;多用途互联网邮件扩展&#xff0c;主要用作电子邮件标准&#xff09;中…

js通过身份证获取年龄

// 获取用户的身份证号码let identityCard this.idNum.replace(/\s/g, "");//判断长度let len identityCard.length;//设置新的变量var strBirthday "";//根据长度获取年月日if (len 18) {strBirthday identityCard.substr(6, 4) "/" identi…

爬取豆瓣top250

#xpath #第一种方法 可在开发者工具中找到标签&#xff0c;右键copy xpath&#xff0c;有时需去掉tbody标签 #第二种方法 简单学习xpath&#xff0c;自己书写&#xff0c;掌握基本语法即可&#xff0c;简单的层级关系#先将csv文件以记事本打开&#xff0c;更改编码为ASNI&…

TCP/IP,Http,Socket,XMPP的区别

网络由下往上分为 物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。 通过初步的了解&#xff0c;我知道IP协议对应于网络层&#xff0c;TCP协议对应于传输层&#xff0c;而HTTP协议对应于应用层&#xff0c; 三者从本质上来说没有可比性&#xff0c; socket则是对…

LED音乐频谱之点阵

转载请注明出处&#xff1a;http://blog.csdn.net/ruoyunliufeng/article/details/37967455 一.硬件 这里的LED选择直插的雾面LED&#xff0c;亮度可以还不失美观。注意每行要加上限流电阻。74HC138&#xff08;三八译码器&#xff09;作为列选&#xff0c;每行都连着74HC595&a…

上架相关——App Store 上架流程

说实话&#xff0c;公司要上架一个自己做的一个小项目。为了完成这个任务&#xff0c;菜鸟的我一遍找资料一遍跟着做&#xff0c;一遍修改错误一遍查找解决方案。网上的资料大部分都是2015年以前的资料&#xff0c;资料有点不够过时&#xff0c;而且步骤配图也不是很详细&#…

this.$router 的三种跳转页面方法

第一种&#xff1a; this.$router.push(需要跳转到的路径名称)此方法跳转后&#xff0c;会在历史栏目中保存路劲地址&#xff0c;当点击历史标签时可以进行访问 第二种&#xff1a; this.$router.replace(需要跳转到的路径名称)此方法跳转后&#xff0c;会在历史栏目中不保存…

cf777c

题意&#xff1a;给你一个n*m的数阵 对于一行到另一行&#xff0c;若存在一列从上到下递减&#xff0c;则称之符合题意 The first line of the input contains two positive integers n and m (1 ≤ nm ≤ 100 000) — the number of rows and the number of columns in t…

上架相关——appstore 更新app版本

注&#xff1a;此片文章是基于app已经上架&#xff0c;也就是证书都已经配置好的前提下。 首先是还是app打包 修改版本号 修改project处的pp文件 检查无误后打包打包完成后upload to app store 漫长的等待。。 上传到appstore进入iTunesConnect 选择我的app 选择对应app点…

输入框输入数字,且不能有小数点存在

基于Vue项目进行设置 <template><comp v-if"update"></comp><button click"reload()">刷新comp组件</button></template><script>import comp from /views/comp.vueexport default {name: parentComp,data() {r…