前言

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优化做一些小小的总结,当然不一定很全面,如果你有更好的建议欢迎在评论里面做补充哈。

关于Socket的经验小结的更多相关文章

  1. Unity3d 经验小结

      Unity3d 经验小结 文本教程 你是第2541个围观者 0条评论 供稿者:Jamesgary 标签:unity3d教程 Fbx.贴图导入Unity时的注意事项: 在导出Fbx之前,Maya中已 ...

  2. DevExpress使用教程:GridView经验小结(官方中文文献经典资料技巧)

    下面是笔者自己总结的使用 DevExpress Gridview 的一些经验小结,分享给大家: 1.去除 GridView 头上的 "Drag a column header here to ...

  3. andriod socket开发问题小结

    andriod socket开发问题小结 个人信息:就读于燕大本科软件project专业 眼下大四; 本人博客:google搜索"cqs_2012"就可以; 个人爱好:酷爱数据结构 ...

  4. Nginx 经验小结

    chmod 777 永远不要 使用 777,有时候可以懒惰的解决权限问题, 但是它同样也表示你没有线索去解决权限问题,你只是在碰运气. 你应该检查整个路径的权限,并思考发生了什么事情. 把 root ...

  5. 基于webmagic的爬虫项目经验小结

    大概在1个月前,利用webmagic做了一个爬虫项目,下面是该项目的一些个人心得,贴在这里备份: 一.为什么选择webmagic? 说实话,开源的爬虫框架已经很多了,有各种语言(比如:python.j ...

  6. IE8+兼容经验小结

    最近一段时间,我都使用Flask+Bootstrap3的框架组合进行开发.本文就是在这种技术组合下,分享IE8+兼容性问题的解决方法.根据我的实践经验,如果你在写HTML/CSS时候是按照W3C推荐的 ...

  7. WP小游戏产品海外发行经验小结

    在群里和大家聊天的时候,大家最多抱怨的就是国内WP份额低,辛辛苦苦做的APP变现困难.我和大家一样,兼职做一些开发,不过我的APP主要面向的是海外市场,从5月份上线到现在不到两个月的时间,没有花费一分 ...

  8. 1300多万条数据30G论坛大数据优化实战经验小结

    最近由于某大型网站社区论坛运行效率比较低用户反馈论坛有些卡需要对系统进行优化,论坛性能影响了公司的形象还有网站的流量,当然这也会影响到公司的收入,而且后期还需要长期维护网站的社区论坛服务. 1:并发访 ...

  9. Windows网络编程经验小结

    转自:CSDN网友的强贴,其ID:gdy119 (夜风微凉) 1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)调用closesocket(一般不会立即关闭而 ...

随机推荐

  1. [ios][swift]swift混编

    http://blog.csdn.net/iflychenyang/article/details/8876542(如何在Objective-C的头文件引用C++的头文件) 1.将.m文件扩展名改为. ...

  2. 每日一九度之 题目1040:Prime Number

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:6732 解决:2738 题目描述: Output the k-th prime number. 输入: k≤10000 输出: The k- ...

  3. Quick-cocos2d-x v3.3 SocketTCP链接(转)

    Quick-Cocos2d-x v3.3里面提供了两种长连接WebSockets.SocketTCP,这里说一下SocketTCP的用法. 1 2 3 local net = require(&quo ...

  4. 【转】Program Files (x86)文件夹是干什么的

    转载地址:http://leonax.net/p/2373/what-program-files-x86-folder-for/ 今天在微博上看到有人问这个问题.在装好64位的Windows之后,能在 ...

  5. 20150625_Andriod_01_ListView1_条目显示

    android listview 参考地址: http://www.cnblogs.com/zhengbeibei/archive/2013/05/14/3078805.html  http://xy ...

  6. 在opencv中实现中文输出

    http://pan.baidu.com/s/1hrQTWDe 已经成功 ; 来自为知笔记(Wiz)

  7. Unity Adam特性整理

    1.Wind 小工具,一个绘制箭头Gizmos的脚本 2.TubeLight柱形光照 蛮NB的技术,实现动态柱状光照,但相机必须挂上PostProcessing 默认场景拖出来之后是这样的,然后给相机 ...

  8. linux下如何安装配置redis及主从配置

    redis的优点:支持主从备份,操作指令丰富,支持异步的数据持久化 将 redis 安装到 /usr/local/webserver/redis 1.下载安装包 wget http://redis.g ...

  9. sql 第 10条 到20条

    sql 第 10条 到20条 select * from( select *,ROW_NUMBER () over (order by @@servername) as rownum from tb_ ...

  10. 光耦PC817

    光耦一共4个引脚,两个输入,两个输出. 输入接5v和gnd,5v接时加100欧姆电阻.输出不大于35v电压,这时输出端通路(只是通路,不是短路)