关于Socket的经验小结
前言
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的经验小结的更多相关文章
- Unity3d 经验小结
Unity3d 经验小结 文本教程 你是第2541个围观者 0条评论 供稿者:Jamesgary 标签:unity3d教程 Fbx.贴图导入Unity时的注意事项: 在导出Fbx之前,Maya中已 ...
- DevExpress使用教程:GridView经验小结(官方中文文献经典资料技巧)
下面是笔者自己总结的使用 DevExpress Gridview 的一些经验小结,分享给大家: 1.去除 GridView 头上的 "Drag a column header here to ...
- andriod socket开发问题小结
andriod socket开发问题小结 个人信息:就读于燕大本科软件project专业 眼下大四; 本人博客:google搜索"cqs_2012"就可以; 个人爱好:酷爱数据结构 ...
- Nginx 经验小结
chmod 777 永远不要 使用 777,有时候可以懒惰的解决权限问题, 但是它同样也表示你没有线索去解决权限问题,你只是在碰运气. 你应该检查整个路径的权限,并思考发生了什么事情. 把 root ...
- 基于webmagic的爬虫项目经验小结
大概在1个月前,利用webmagic做了一个爬虫项目,下面是该项目的一些个人心得,贴在这里备份: 一.为什么选择webmagic? 说实话,开源的爬虫框架已经很多了,有各种语言(比如:python.j ...
- IE8+兼容经验小结
最近一段时间,我都使用Flask+Bootstrap3的框架组合进行开发.本文就是在这种技术组合下,分享IE8+兼容性问题的解决方法.根据我的实践经验,如果你在写HTML/CSS时候是按照W3C推荐的 ...
- WP小游戏产品海外发行经验小结
在群里和大家聊天的时候,大家最多抱怨的就是国内WP份额低,辛辛苦苦做的APP变现困难.我和大家一样,兼职做一些开发,不过我的APP主要面向的是海外市场,从5月份上线到现在不到两个月的时间,没有花费一分 ...
- 1300多万条数据30G论坛大数据优化实战经验小结
最近由于某大型网站社区论坛运行效率比较低用户反馈论坛有些卡需要对系统进行优化,论坛性能影响了公司的形象还有网站的流量,当然这也会影响到公司的收入,而且后期还需要长期维护网站的社区论坛服务. 1:并发访 ...
- Windows网络编程经验小结
转自:CSDN网友的强贴,其ID:gdy119 (夜风微凉) 1. 如果在已经处于 ESTABLISHED状态下的socket(一般由端口号和标志符区分)调用closesocket(一般不会立即关闭而 ...
随机推荐
- SpringMVC 接收ajax发送的数组对象
本文粘贴自:http://my.oschina.net/jiefalcon/blog/384153?fromerr=24Lewn46 [转]SpringMVC @RequestBody接收Json对象 ...
- 【转】Firefox快捷键
转载地址: http://www.douban.com/note/140139119/ Ctrl + 数字键来打开第N个标签页这种还要先数完再到键盘上找数字Ctrl + Page Up = 激活左边一 ...
- Lambda表达式 一些基本用法
带条件的:IQueryable<UserInfo> demo=db.UserInfo.where<UserInfo>(u=>u.ID>2); 取指定列数据:var ...
- YTU 2987: 调整表中元素顺序(线性表)
2987: 调整表中元素顺序(线性表) 时间限制: 1 Sec 内存限制: 2 MB 提交: 1 解决: 1 题目描述 若一个线性表L采用顺序存储结构存储,其中所有元素都为整数.设计一个算法,将所 ...
- Android网络通信之WiFi Direct
使用Wi-Fi Direct技术可以让具备硬件支持的设备在没有中间接入点的情况下进行直接互联.Android 4.0(API版本14)及以后的系统都提供了对Wi-Fi Direct的API支持.通过对 ...
- Java内部类、静态嵌套类、局部内部类、匿名内部类
Nested classes are further divided into two types: static nested classes: If the nested class is sta ...
- Linux 多线程应用中如何编写安全的信号处理函数
http://blog.163.com/he_junwei/blog/static/1979376462014021105242552/ http://www.ibm.com/developerwor ...
- 局域网无法访问vmware虚拟机WEB服务器解决办法
环境:虚拟机服务器是centos,apache+php+mysql环境,但是局域网无法访问 1.本机能ping通虚拟机 2.虚拟机也能ping通本机 3.虚拟机能访问自己的web 4.本机无法访问虚拟 ...
- 2016年12月2日 星期五 --出埃及记 Exodus 20:23
2016年12月2日 星期五 --出埃及记 Exodus 20:23 Do not make any gods to be alongside me; do not make for yourselv ...
- Intent官方教程(2)Intent的两种类型
Intent Types There are two types of intents: Explicit intents specify the component to start by name ...