package com.anyun.im_lib.netty;
|
|
import android.text.TextUtils;
|
import android.util.Log;
|
|
import com.anyun.im_lib.ExecutorServiceFactory;
|
import com.anyun.im_lib.HeartbeatHandler;
|
import com.anyun.im_lib.IMSConfig;
|
import com.anyun.im_lib.MsgDispatcher;
|
import com.anyun.im_lib.MsgTimeOutTimerManager;
|
import com.anyun.im_lib.interf.IMSClientInteface;
|
import com.anyun.im_lib.listener.IMSConnectStatusCallback;
|
import com.anyun.im_lib.listener.OnEventListener;
|
|
import java.util.Vector;
|
|
import io.netty.bootstrap.Bootstrap;
|
import io.netty.channel.Channel;
|
import io.netty.channel.ChannelOption;
|
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.nio.NioEventLoopGroup;
|
import io.netty.channel.socket.nio.NioSocketChannel;
|
import io.netty.handler.timeout.IdleStateHandler;
|
|
|
/**
|
* MyApplication2
|
* Created by lzw on 2019/12/2. 13:14:52
|
* 邮箱:632393724@qq.com
|
* All Rights Saved! Chongqing AnYun Tech co. LTD
|
*/
|
public class NettyTcpClient implements IMSClientInteface {
|
|
private static final String TAG = NettyTcpClient.class.getSimpleName();
|
|
private static volatile NettyTcpClient instance;
|
|
private Bootstrap bootstrap;
|
private Channel channel;
|
|
/****标识IMS是否已关闭***/
|
private boolean isClosed = false;
|
|
/****ims服务器地址组***/
|
private Vector<String> serverUrlList;
|
|
/****与应用层交互的listener***/
|
private OnEventListener mOnEventListener;
|
/*** ims连接状态回调监听器**/
|
private IMSConnectStatusCallback imsConnectStatusCallback;
|
|
/****消息转发器***/
|
private MsgDispatcher msgDispatcher;
|
|
/***线程池工厂**/
|
private ExecutorServiceFactory loopGroup;
|
|
/****是否正在进行重连***/
|
private boolean isReconnecting = false;
|
/*** ims连接状态,初始化为连接失败 ***/
|
private int connectStatus = IMSConfig.CONNECT_STATE_FAILURE;
|
|
/*** 重连间隔时长 ***/
|
private int reconnectInterval = IMSConfig.DEFAULT_RECONNECT_BASE_DELAY_TIME;
|
/*** 连接超时时长 ***/
|
private int connectTimeOut = IMSConfig.DEFAULT_CONNECT_TIMEOUT;
|
|
private int heartBeatInterval = IMSConfig.DEFAULT_HEARTBEAT_INTERVAL_FOREGROUND;
|
private int foregroundHeartBeatInterval = IMSConfig.DEFAULT_HEARTBEAT_INTERVAL_FOREGROUND;
|
private int backgroundHeartBeatInterval = IMSConfig.DEFAULT_HEARTBEAT_INTERVAL_BACKGROUND;
|
|
private int appStatus = IMSConfig.APP_STATUS_FOREGROUND;
|
/*** 消息发送超时重发次数***/
|
private int resendCount = IMSConfig.DEFAULT_RESEND_COUNT;
|
/*** 消息发送失败重发间隔时长***/
|
private int resendInterval = IMSConfig.DEFAULT_RESEND_INTERVAL;
|
|
/*** 当前连接host***/
|
private String currentHost = null;
|
/*** 当前连接port***/
|
private int currentPort = -1;
|
/*** 消息发送超时定时管理器***/
|
private MsgTimeOutTimerManager msgTimeOutTimerManager;
|
|
|
private NettyTcpClient(){
|
|
}
|
|
public static IMSClientInteface getInstance() {
|
if (null == instance){
|
synchronized (NettyTcpClient.class){
|
if (null==instance){
|
instance = new NettyTcpClient();
|
}
|
}
|
}
|
return instance;
|
}
|
|
/**
|
* 初始化
|
* @param serverUrlList 服务器地址列表
|
* @param listener 与应用层交互的listener
|
* @param callback ims连接状态回调
|
*/
|
@Override
|
public void init(Vector<String> serverUrlList, OnEventListener listener, IMSConnectStatusCallback callback) {
|
close();
|
isClosed = false;
|
this.serverUrlList = serverUrlList;
|
this.mOnEventListener = listener;
|
this.imsConnectStatusCallback = callback;
|
msgDispatcher = new MsgDispatcher();
|
msgDispatcher.setOnEventListener(listener);
|
loopGroup = new ExecutorServiceFactory();
|
loopGroup.initBossLoopGroup();
|
msgTimeOutTimerManager = new MsgTimeOutTimerManager(this);
|
/*** 进行第一次连接***/
|
resetConnect(true);
|
}
|
|
@Override
|
public void resetConnect() {
|
this.resetConnect(false);
|
}
|
|
@Override
|
public void resetConnect(boolean isFirst) {
|
if (!isFirst){
|
try {
|
Thread.sleep(IMSConfig.DEFAULT_RECONNECT_INTERVAL);
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
}
|
//只有第一个调用者才能赋值并调用重连
|
if (!isClosed && !isReconnecting){
|
synchronized (this){
|
if (!isClosed && !isReconnecting){
|
//标识重连任务进行中...
|
isReconnecting = true;
|
|
onConnectStatusCallback(IMSConfig.CONNECT_STATE_CONNECTING);
|
closeChannel();
|
loopGroup.execBossTask(new ResetConnectRunnable(isFirst));
|
}
|
}
|
}
|
}
|
|
private class ResetConnectRunnable implements Runnable{
|
private boolean isFirst;
|
public ResetConnectRunnable(boolean isFirst) {
|
this.isFirst = isFirst;
|
}
|
|
@Override
|
public void run() {
|
if (!isFirst){
|
onConnectStatusCallback(IMSConfig.CONNECT_STATE_FAILURE);
|
}
|
try {
|
//重连时,释放工作组线程池,也就是停止心跳
|
loopGroup.destroyWorkLoopGroup();
|
while (!isClosed){
|
if (!isNetworkAvaliable()){
|
try {
|
Thread.sleep(2000);
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
continue;
|
}
|
//网络可用才进行连接
|
int status;
|
if ((status=reConnect()) == IMSConfig.CONNECT_STATE_SUCCESSFUL){
|
onConnectStatusCallback(status);
|
//连接成功,调出循环
|
break;
|
}
|
|
if (status == IMSConfig.CONNECT_STATE_FAILURE){
|
onConnectStatusCallback(status);
|
try {
|
Thread.sleep(IMSConfig.DEFAULT_RECONNECT_INTERVAL);
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
}
|
}
|
} finally {
|
//标识重连任务停止
|
isReconnecting = false;
|
}
|
}
|
}
|
|
/**
|
* 重连,首次连接也认为是第一次重连
|
* @return
|
*/
|
private int reConnect() {
|
if (!isClosed){
|
try {
|
//先释放EventLoop线程组
|
if (bootstrap != null){
|
bootstrap.group().shutdownGracefully();
|
}
|
} finally {
|
bootstrap = null;
|
}
|
initBootstrap();
|
return connectServer();
|
}
|
return IMSConfig.CONNECT_STATE_FAILURE;
|
}
|
|
/***
|
* 初始化Bootstrap
|
*/
|
private void initBootstrap() {
|
EventLoopGroup loopGroup = new NioEventLoopGroup(4);
|
bootstrap = new Bootstrap();
|
bootstrap.group(loopGroup).channel(NioSocketChannel.class);
|
// 设置该项以后,如果在两小时内没有数据通信时,TCP会自动发送一个活动探测数据报文
|
bootstrap.option(ChannelOption.SO_KEEPALIVE,true);
|
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,getConnectTimeout());
|
bootstrap.handler(new TCPChannelInitializerHandler(this));
|
}
|
|
private int connectServer(){
|
if (serverUrlList == null || serverUrlList.size() == 0){
|
return IMSConfig.CONNECT_STATE_FAILURE;
|
}
|
for (int i = 0; i < serverUrlList.size(); i++) {
|
String serverUrl = serverUrlList.get(i);
|
if (TextUtils.isEmpty(serverUrl)){
|
return IMSConfig.CONNECT_STATE_FAILURE;
|
}
|
}
|
}
|
|
private boolean isNetworkAvaliable() {
|
if (mOnEventListener != null){
|
return mOnEventListener.isNetWorkAvailable();
|
}
|
return false;
|
}
|
|
private void onConnectStatusCallback(int connectStateConnecting) {
|
}
|
|
@Override
|
public void close() {
|
if (isClosed){
|
return;
|
}
|
isClosed = true;
|
/*** 关闭channel***/
|
try {
|
closeChannel();
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
|
/*** 关闭bootstrap ***/
|
try {
|
if (bootstrap != null){
|
bootstrap.group().shutdownGracefully();
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
/*** 释放线程池 ***/
|
try {
|
if (loopGroup != null){
|
loopGroup.destroy();
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
} finally {
|
try {
|
if (serverUrlList != null){
|
serverUrlList.clear();
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
isReconnecting = false;
|
channel = null;
|
bootstrap = null;
|
}
|
}
|
|
private void closeChannel() {
|
if (channel != null){
|
removeHandler(HeartbeatHandler.class.getSimpleName());
|
removeHandler(TCPReadHandler.class.getSimpleName());
|
removeHandler(IdleStateHandler.class.getSimpleName());
|
}
|
}
|
|
/**
|
* 移除指定handler
|
* @param handlerName
|
*/
|
private void removeHandler(String handlerName) {
|
try {
|
if (channel.pipeline().get(handlerName) != null){
|
channel.pipeline().remove(handlerName);
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
Log.i(TAG, "removeHandler fail,handlerName="+handlerName);
|
}
|
}
|
|
@Override
|
public boolean isClosed() {
|
return false;
|
}
|
|
@Override
|
public void sendMsg(String msg) {
|
|
}
|
|
@Override
|
public void sendMsg(String msg, boolean isJoinTimeoutManager) {
|
|
}
|
|
@Override
|
public int getReconnectInterval() {
|
return 0;
|
}
|
|
@Override
|
public int getConnectTimeout() {
|
return 0;
|
}
|
|
@Override
|
public int getForegroundHeartbeatInterval() {
|
return 0;
|
}
|
|
@Override
|
public void setAppStatus(int appStatus) {
|
|
}
|
|
@Override
|
public int getBackgroundHeartbeatInterval() {
|
return 0;
|
}
|
|
@Override
|
public int getResendCount() {
|
return 0;
|
}
|
|
@Override
|
public int getResendInterval() {
|
return 0;
|
}
|
|
@Override
|
public MsgDispatcher getMsgDispatcher() {
|
return null;
|
}
|
|
@Override
|
public MsgTimeOutTimerManager getMsgTimeOutTimerManager() {
|
return null;
|
}
|
}
|