默认
打赏 发表评论 14
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
跟着源码学IM(二):自已开发IM很难?手把手教你撸一个Andriod版IM
微信扫一扫关注!

本文由作者FreddyChen原创分享,为了更好的体现文章价值,即时通讯网引用时有少许改动,感谢原作者。


0、系列文章



1、写在前面


一直想写一篇关于im即时通讯分享的文章,无奈工作太忙,很难抽出时间。今天终于从公司离职了,打算好好休息几天再重新找工作,趁时间空闲,决定静下心来写一篇文章,毕竟从前辈那里学到了很多东西。

工作了五年半,这三四年来一直在做社交相关的项目,有直播、即时通讯、短视频分享、社区论坛等产品,深知即时通讯技术在一个项目中的重要性,本着开源分享的精神,也趁这机会总结一下,所以写下了这篇文章。

* 重要提示:本文不是一篇即时通讯理论文章,文章内容全部由实战代码组织而成,如果你对即时通讯(IM)技术理论了解的太少,建议先详细阅读:《新手入门一篇就够:从零开发移动端IM》。本站的另几篇《拿起键盘就是干:跟我一起徒手开发一套分布式IM系统》、《适合新手:从零开发一个IM服务端(基于Netty,有完整源码)》、《适合新手:手把手教你用Go快速搭建高性能、可扩展的IM系统(有源码)》也值得一读。

本文实践内容将涉及以下即时通讯技术内容:

  • 1)Protobuf序列化;
  • 2)TCP拆包与粘包;
  • 3)长连接握手认证;
  • 4)心跳机制;
  • 5)重连机制;
  • 6)消息重发机制;
  • 7)读写超时机制;
  • 8)离线消息;
  • 9)线程池。

不想看文章的同学,可以直接到Github下载本文源码:


接下来,让我们进入正题。

2、本文阅读对象


本文适合没有任何即时通讯(IM)开发经验的小白开发者阅读,文章将教你从零开始,围绕一个典型即时通讯(IM)系统的方方面面,手把手为你展示如何基于Netty+TCP+Protobuf来开发出这样的系统。非常适合从零入门的Android开发者。

本文不适合没有编程的准开发者阅读,因为即时通讯(IM)系统属于特定的业务领域,如果你连一般的逻辑代码都很难编写出来,不建议阅读本文。本文显然不是一个编程语言入门教程。

另外:本站的另一篇同类文章《拉起键盘就是干:跟我一起徒手撸一套分布式IM系统》也值得一读。

3、关于作者


WX20190721-165501@2x.jpg
本文原文内容由FreddyChen原创分享,作者现从事Android程序开发,他的技术博客地址:https://juejin.im/user/5bd7affbe51d4547f763fe72

4、为什么使用TCP?


这里需要简单解释一下,TCP/UDP的区别,简单地总结一下。

优点:

  • 1)TCP:优点体现在稳定、可靠上,在传输数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完之后,还会断开连接用来节约系统资源。
  • 2)UDP:优点体现在快,比TCP稍安全,UDP没有TCP拥有的各种机制,是一个无状态的传输协议,所以传递数据非常快,没有TCP的这些机制,被攻击利用的机制就少一些,但是也无法避免被攻击。

缺点:

  • 1)TCP:缺点就是慢,效率低,占用系统资源高,易被攻击,TCP在传递数据之前要先建立连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞机制等都会消耗大量时间,而且要在每台设备上维护所有的传输连接。
  • 2)UDP:缺点就是不可靠,不稳定,因为没有TCP的那些机制,UDP在传输数据时,如果网络质量不好,就会很容易丢包,造成数据的缺失。

适用场景:

  • 1)TCP:当对网络通讯质量有要求时,比如HTTP、HTTPS、FTP等传输文件的协议, POP、SMTP等邮件传输的协议。
  • 2)UDP:对网络通讯质量要求不高时,要求网络通讯速度要快的场景。

至于WebSocket,后续可能会专门写一篇文章来介绍。综上所述,决定采用TCP协议

关于TCP和UDP的对比和选型的详细文章,请见:


或者,如果你对TCP、UDP协议了解的太少,可以阅读一下文章:


5、为什么使用Protobuf?


对于App网络传输协议,我们比较常见的、可选的,有三种,分别是json/xml/protobuf,老规矩,我们先分别来看看这三种格式的优缺点。

PS:如果你不了解protobuf是什么,建议详细阅读:《Protobuf通信协议详解:代码演示、详细原理介绍等》。

优点:

  • 1)json:优点就是较XML格式更加小巧,传输效率较xml提高了很多,可读性还不错。
  • 2)xml:优点就是可读性强,解析方便。
  • 3)protobuf:优点就是传输效率快(据说在数据量大的时候,传输效率比xml和json快10-20倍),序列化后体积相比Json和XML很小,支持跨平台多语言,消息格式升级和兼容性还不错,序列化反序列化速度很快。

缺点:

  • 1)json:缺点就是传输效率也不是特别高(比xml快,但比protobuf要慢很多)。
  • 2)xml:缺点就是效率不高,资源消耗过大。
  • 3)protobuf:缺点就是使用不太方便。

在一个需要大量的数据传输的场景中,如果数据量很大,那么选择protobuf可以明显的减少数据量,减少网络IO,从而减少网络传输所消耗的时间。考虑到作为一个主打社交的产品,消息数据量会非常大,同时为了节约流量,所以采用protobuf是一个不错的选择。

更多有关IM相关的协议格式选型方面的文章,可进一步阅读:

>> 更多同类文章 ……

6、为什么使用Netty?


首先,我们来了解一下,Netty到底是个什么东西。网络上找到的介绍:Netty是由JBOSS提供的基于Java NIO的开源框架,Netty提供异步非阻塞、事件驱动、高性能、高可靠、高可定制性的网络应用程序和工具,可用于开发服务端和客户端。

PS:如果你对Java的经典IO、NIO或者Netty框架不了解,请阅读以下文章:


为什么不用Java BIO?

  • 1)一连接一线程:由于线程数是有限的,所以这样非常消耗资源,最终也导致它不能承受高并发连接的需求。
  • 2)性能低:因为频繁的进行上下文切换,导致CUP利用率低。
  • 3)可靠性差:由于所有的IO操作都是同步的,即使是业务线程也如此,所以业务线程的IO操作也有可能被阻塞,这将导致系统过分依赖网络的实时情况和外部组件的处理能力,可靠性大大降低。

为什么不用Java NIO?

  • 1)NIO的类库和API相当复杂,使用它来开发,需要非常熟练地掌握Selector、ByteBuffer、ServerSocketChannel、SocketChannel等。
  • 2)需要很多额外的编程技能来辅助使用NIO,例如,因为NIO涉及了Reactor线程模型,所以必须必须对多线程和网络编程非常熟悉才能写出高质量的NIO程序。
  • 3)想要有高可靠性,工作量和难度都非常的大,因为服务端需要面临客户端频繁的接入和断开、网络闪断、半包读写、失败缓存、网络阻塞的问题,这些将严重影响我们的可靠性,而使用原生NIO解决它们的难度相当大。
  • 4)JDK NIO中著名的BUG--epoll空轮询,当select返回0时,会导致Selector空轮询而导致CUP100%,官方表示JDK1.6之后修复了这个问题,其实只是发生的概率降低了,没有根本上解决。

为什么用Netty?

  • 1)API使用简单,更容易上手,开发门槛低;
  • 2)功能强大,预置了多种编解码功能,支持多种主流协议;
  • 3)定制能力高,可以通过ChannelHandler对通信框架进行灵活地拓展;
  • 4)高性能,与目前多种NIO主流框架相比,Netty综合性能最高;
  • 5)高稳定性,解决了JDK NIO的BUG;
  • 6)经历了大规模的商业应用考验,质量和可靠性都有很好的验证。

为什么不用第三方SDK,如:融云、环信、腾讯TIM?

这个就见仁见智了,有的时候,是因为公司的技术选型问题,因为用第三方的SDK,意味着消息数据需要存储到第三方的服务器上,再者,可扩展性、灵活性肯定没有自己开发的要好,还有一个小问题,就是收费。比如,融云免费版只支持100个注册用户,超过100就要收费,群聊支持人数有限制等等...

1.jpg
▲ 以上截图内容来自某云IM官网

Mina其实跟Netty很像,大部分API都相同,因为是同一个作者开发的。但感觉Mina没有Netty成熟,在使用Netty的过程中,出了问题很轻易地可以找到解决方案,所以,Netty是一个不错的选择。

PS:有关MINA和Netty框架的关系和对比,详见以下文章:


好了,废话不多说,直接开始吧。

7、准备工作


首先,我们新建一个Project,在Project里面再新建一个Android Library,Module名称暂且叫做im_lib,如图所示:
7-1.jpg

然后,分析一下我们的消息结构,每条消息应该会有一个消息唯一id,发送者id,接收者id,消息类型,发送时间等,经过分析,整理出一个通用的消息类型,如下:

  • msgId:消息id
  • fromId:发送者id
  • toId:接收者id
  • msgType:消息类型
  • msgContentType:消息内容类型
  • timestamp:消息时间戳
  • statusReport:状态报告
  • extend:扩展字段

根据上述所示,我整理了一个思维导图,方便大家参考:
7-2.jpg

上图因原图尺寸较大,如果看不清的话,请下载清晰大图:
消息结构-思维导图[清晰大图].zip (314.71 KB , 下载次数: 191 , 售价: 1 金币)

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:[已解决] 求助MobileIMSDK退出账号后,重新登录会报203错误下一篇:从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路

本帖已收录至以下技术专辑

推荐方案
评论 14
收藏啦,以后想做安卓的时候再来学习,虽然我现在做的是c++,但我也有一颗做java的心
引用:李瑞啊 发表于 2019-07-22 08:37
收藏啦,以后想做安卓的时候再来学习,虽然我现在做的是c++,但我也有一颗做java的心

后面怕是得玩Kotlin了
引用:JackJiang 发表于 2019-07-22 09:40
后面怕是得玩Kotlin了


人生苦短,我学python
引用:踏雪寻梅 发表于 2019-07-23 08:57
人生苦短,我学python

现在的工作是做大数据还是AI?
引用:JackJiang 发表于 2019-07-23 09:05
现在的工作是做大数据还是AI?

闲时看看罢了
引用:踏雪寻梅 发表于 2019-07-23 08:57
人生苦短,我学python

不知道为啥,对python又很大的抵触感,可能是因为我以前是做c的吧
好详细
你好,我是初次学习netty,在根据这篇文章搭建一个简单的IM通讯系统时,需要一个问题:“客户端和服务端首次可以正常发送1001握手消息,进行链接,但是,客户端在断网以后,重新链接网络,这时候进行重连操作,发送1001握手消息时,服务端可以收到客户端发送的握手消息,并且能够成功返回对应的ark消息,但是,客户端却一直收不到这个ark消息,因此导致客户端无法发送心跳消息。”,所以想请问一下,这种情况是什么原因导致的啊,又该怎么处理呢?----谢谢
你好,我是初次学习netty,在根据这篇文章(http://www.52im.net/forum.php?mod=viewthread&tid=2671)搭建一个简单的IM通讯系统时,需要一个问题:“客户端和服务端首次可以正常发送1001握手消息,进行链接,但是,客户端在断网以后,重新链接网络,这时候进行重连操作,发送1001握手消息时,服务端可以收到客户端发送的握手消息,并且能够成功返回对应的ark消息,但是,客户端却一直收不到这个ark消息,因此导致客户端无法发送心跳消息。”,所以想请问一下,这种情况是什么原因导致的啊,又该怎么处理呢?----谢谢
引用:LeeTg 发表于 2020-05-19 09:07
你好,我是初次学习netty,在根据这篇文章(http://www.52im.net/forum.php?mod=viewthread&tid=2671)搭建 ...

这个文章以及它的带码主要是学习用的,没有经过生产使用,经不起推敲。

你要么研究一下MobileIMSDK框架https://github.com/JackJiang2011/MobileIMSDK
引用:JackJiang 发表于 2020-05-19 10:43
这个文章以及它的带码主要是学习用的,没有经过生产使用,经不起推敲。

你要么研究一下MobileIMSDK框 ...

恩恩,我只是自己初次学习netty,在学习过程中遇到的问题,一直不明白是什么原因导致,大神,可不可以解惑一下呢?
这年头这么好的资料很少了
签名: 为技术论坛点赞
不错不错,学习第二天
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部