im-android-dome

本文主要实现能主动发送握手和心跳的客户端控制台dome,记录一下自己的学习过程。

强烈建议看下面这篇文章

参考文章和代码(代码详情请看这个):跟着源码学IM(二):自已开发IM很难?手把手教你撸一个Andriod版IM-IM开发/专项技术区 - 即时通讯开发者社区!

基于TCP和Protobuf作为通信协议,使用netty构建客户端服务 

Protocol Buffers 是一种轻便高效的结构化数据存储格式,用于数据的序列化,很快,很小,比json更牛,深受广大消费者喜爱

效果如图

项目结构如图。使用netty,客户端的主要是一些handler的编写

1. 准备工作

创建项目,添加依赖

创建新项目,添加新module(Android Library),设置名称 im_lib 目录如图

分析消息结构,编写.proto文件,编译 .proto 文件之前需要下载protobuf 编译器,配置环境变量

syntax = "proto3";// 指定protobuf版本
option java_package = "com.sy.im.protobuf";// 指定包名
option java_outer_classname = "MessageProtobuf";// 指定生成的类名

message Msg {
    Head head = 1;// 消息头
    string body = 2;// 消息体
}

message Head {
    string msgId = 1;// 消息id
    int32 msgType = 2;// 消息类型
    int32 msgContentType = 3;// 消息内容类型
    string fromId = 4;// 消息发送者id
    string toId = 5;// 消息接收者id
    int64 timestamp = 6;// 消息时间戳
    int32 statusReport = 7;// 状态报告
    string extend = 8;// 扩展字段,以key/value形式存放的json
}

这里msg.proto文件是在com目录下的,右键-Open In-Terminal,输入指令,执行生成java文件

protoc --java_out=../ msg.proto

统一依赖管理,这里使用buildSrc,详情

im_ilb/build.gradle,引入依赖

compileOnly "com.alibaba:fastjson:1.2.49"
api "com.google.protobuf:protobuf-java:3.24.4"
compileOnly files('libs/netty-tcp-4.1.33-1.0.jar')

把 netty-tcp-4.1.33-1.0.jar添加到im_lib/libs目录下

gradle sync,准备工作结束,开始业务逻辑的编写

2. 接口,封装

编写接口是为了把业务抽象出来,对代码进行解耦

对 im 服务的客户端进行分析有
1. 初始化方法
2. 建立和关闭连接
3. 发送消息

新建 com/sy/im/interf/IMSClient.java(详情请看源码)

public interface IMSClient {

    /**
     * ims 初始化
     * @param serverUrlList 服务器地址列表
     * @param listener      事件监听器
     * @param callback      ims连接状态回调
     */
    void init(CopyOnWriteArrayList serverUrlList,OnEventListener listener, IMSConnectStatusCallback callback);

    void resetConnect(); // 重置连接

    void resetConnect(boolean isFirst); // 是否第一次连接

    void close(); // 关闭连接,释放资源

    boolean isClosed(); // ims状态

    void sendMsg(MessageProtobuf.Msg msg);// 发送消息

    void sendMsg(MessageProtobuf.Msg msg, boolean isJoinTimeoutManager);// 是否添加超时管理器

}

com/sy/im/interf/IMSConnectStatusCallback.java 连接回调
com/sy/im/interf/OnEventListener.java 事件监听器

编写实现类com/sy/im/netty/NettyTcpClient.java 实现接口ISMClient
编写单例模式

    private static volatile  NettyTcpClient instance;

    private NettyTcpClient() {}

    public static NettyTcpClient getInstance(){
        if(null == instance){
            synchronized (NettyTcpClient.class) {
                if (null == instance) {
                    instance = new NettyTcpClient();
                }
            }
        }
        return instance;
    }

创建工厂方法

public class IMSClientFactory {
    public static IMSClient getIMSClient(){
        return NettyTcpClient.getInstance();
    }
}

3. 实现

初始化

NettyTcpClient


添加 MsgDispatcher,ExecutorServiceFactory
MsgDispatcher,消息转发器,接收消息并通过OnEventListener转发消息到应用层
ExecutorServiceFactory,线程池工厂,负责调度重连及心跳线程

连接与重连

重写resetConnect方法,把注意力放到 resetConnect(boolean isFirst)

非首次连接,需要在连接之前多等一会,可能因为当前网络不好
连接时四步如图,双层判断加锁进行并发处理


新添加变量

私有方法onConnectStatusCallback
根据传入的connectStatus,回调callback中的方法

closeChannel

私有内部类 ResetConnectRunnable,执行连接任务

reConnect() — 初始化bootstrap
连接服务器 connectServer(),获得server host和port
调用toServer():

Bootstrap

TCPChannelInitializerHandler:
设置了自定义长度解码器,解决TCP拆包粘报问题

握手消息认证处理

当客户端与服务端长连接建立成功后,客户端主动向服务端发送一条登录认证消息,服务端返回认证结果

客户端发送的认证(握手)消息需要从应用层获取——在 IMSClient,OnEventListener 添加方法getHandshakeMsg()

在connectServer()中channel != null来判断是否连接成功

连接成功后立刻发送握手消息

LoginAuthRespHandler:

接收服务器响应的消息,拉取应用端的握手消息,比较消息类型,相同则查看响应消息中的status是否握手成功

握手成功后立刻发送心跳消息,添加心跳机制管理

消息超时管理

在将心跳之前,先讲一下这个超时重发

MsgTimeoutTimerManager管理MsgTimeoutTimer,添加,移除和重发


MsgTimeoutTimer 一个消息对应一个计时器,对应着一个定时任务

注意初始化

msgTimeoutTimerManager = new MsgTimeoutTimerManager(this);
心跳机制与读写超时
// 添加心跳消息管理
imsClient.addHeartbeatHandler();

IdleStateHandler -> HeartbeatHandler -> TCPReadHandler
HeartbeatHandler


HeartbeatRespHandler 接收心跳响应消息,打印日志

这里心跳部分就结束了,TCPReadHandler 就是消息处理器

当连接失效,出现异常时,触发重连,主要看channelRead

sendMsg

4. 运行调试和总结

先只测试握手消息,服务端
绑定好端口,拆包粘包处理,编解码器
收到消息,判断消息类型,修改extend字段,返回状态

返回的消息现在LoginAuthRespHandler里进行处理

客户端

 IMSClientBootstrap:将数据准备好后,主要是对IMSClient对象的初始化

下面针对初始化参数,创建接口的实现类

IMSEventListener implements OnEventListener
注意,这个类主要负责与应用层交互的
保存私有变量 userId 和 token 

IMSEventListener

服务器收到的

下面这两个方法先顶替一下

IMSConnectStatusListener 直接实现 IMSConnectStatusCallback 接口

调试过程中出现Java版本问题,实际上gradle7.4是能兼容Java8的,可以不用改版本。

然后是不能运行main函数的问题修改一下配置,AndroidStudio执行main方法报错,可以运行测试

下面捋一下刚创建的客户端:

创建IMSClient实例(实现类是NettyTcpClient)

首先初始化连接,如果channel创建成功,那么连接成功,onConnectStatusCallback立刻发送一条握手消息。

进入TCPChannelInitializerHandler,这三个处理器已经创建完成。收到服务端返回的消息后才创建HeartbeatHandler,即握手成功后才添加心跳管理

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值