我们根据自定义的消息类型来编写proto文件。 然后执行命令(我用的mac,windows命令应该也差不多): 然后就会看到,在和proto文件同级目录下,会生成一个java类,这个就是我们需要用到的东东: 我们打开瞄一眼: 东西比较多,不用去管,这是google为我们生成的protobuf类,直接用就行,怎么用呢?直接用这个类文件,拷到我们开始指定的项目包路径下就可以啦: 添加依赖后,可以看到,MessageProtobuf类文件已经没有报错了,顺便把netty的jar包也导进来一下,还有fastjson的: 建议用netty-all-x.x.xx.Final的jar包,后续熟悉了,可以用精简的jar包。
至此,准备工作已结束,下面,我们来编写java代码,实现即时通讯的功能。
封装
为什么需要封装呢?说白了,就是为了解耦,为了方便日后切换到不同框架实现,而无需到处修改调用的地方。举个栗子,比如Android早期比较流行的图片加载框架是Universal ImageLoader,后期因为某些原因,原作者停止了维护该项目,目前比较流行的图片加载框架是Picasso或Glide,因为图片加载功能可能调用的地方非常多,如果不作一些封装,早期使用了Universal ImageLoader的话,现在需要切换到Glide,那改动量将非常非常大,而且还很有可能会有遗漏,风险度非常高。
那么,有什么解决方案呢?
很简单,我们可以用工厂设计模式进行一些封装,工厂模式有三种:简单工厂模式、抽象工厂模式、工厂方法模式。在这里,我采用工厂方法模式进行封装,具体区别,可以参见:通俗讲讲我对简单工厂、工厂方法、抽象工厂三种设计模式的理解
我们分析一下,ims(IM Service,下文简称ims)应该是有初始化、建立连接、重连、关闭连接、释放资源、判断长连接是否关闭、发送消息等功能,基于上述分析,我们可以进行一个接口抽象: OnEventListener是与应用层交互的listener: IMConnectStatusCallback是ims连接状态回调监听器:
然后写一个Netty tcp实现类:
接下来,写一个工厂方法:
封装部分到此结束,接下来,就是实现了。
初始化
我们先实现init(Vector serverUrlList, OnEventListener listener, IMSConnectStatusCallback callback)方法,初始化一些参数,以及进行第一次连接等:
其中,MsgDispatcher是消息转发器,负责将接收到的消息转发到应用层:
ExecutorServiceFactory是线程池工厂,负责调度重连及心跳线程:
连接及重连
resetConnect()方法作为连接的起点,首次连接以及重连逻辑,都是在resetConnect()方法进行逻辑处理,我们来瞄一眼: 可以看到,非首次进行连接,也就是连接一个周期失败后,进行重连时,会先让线程休眠一段时间,因为这个时候也许网络状况不太好,接着,判断ims是否已关闭或者是否正在进行重连操作,由于重连操作是在子线程执行,为了避免重复重连,需要进行一些并发处理。开始重连任务后,分四个步骤执行:
- 改变重连状态标识
- 回调连接状态到应用层
- 关闭之前打开的连接channel
- 利用线程池执行一个新的重连任务
ResetConnectRunnable是重连任务,核心的重连逻辑都放到这里执行: