Kotlin+Netty Android客户端连接修改之前的连接,处理TCP粘包问题与解决方案

[使用Kotlin+Netty Android客户端连接
由于Kotlin也是新的语言,在2017年中下才和Android完全融合
在这里我只写了Netty客户端连接服务器的部分代码,如果想要更多代码可以下方留言
我这个是实用在真实项目中的代码,由于我自己也遇到了很多问题,所以就想分享出来给大家,谢谢

首先在代码中解决了两个重大问题
1.Netty Client启动的时候需要重连
2.在程序运行中连接断掉需要重连。
其他的都不说了直接上代码](https://blog.csdn.net/jeff_yaojie/article/details/79280816)

package com.ftrd.flashlight.nettys

import com.ftrd.flashlight.FileKt.DelegatesExt
import com.ftrd.flashlight.FileKt.FinalValue.COMMAND_IP
import com.ftrd.flashlight.FileKt.FinalValue.COMMAND_PORT
import com.ftrd.flashlight.FileKt.LogUtils;
import com.ftrd.flashlight.nettys.codes.RegisterEncode
import com.ftrd.flashlight.nettys.handlers.DecodeHandler
import com.ftrd.flashlight.nettys.handlers.MsgHandler
import com.goav.netty.Handler.EncodeHandler
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import io.netty.util.CharsetUtil
import io.netty.handler.codec.string.StringDecoder
import io.netty.handler.codec.LengthFieldPrepender
import io.netty.handler.codec.LengthFieldBasedFrameDecoder
import java.nio.charset.Charset


/**
 * @author: Jeff <[email protected]>
 * @date:  2018-02-05 16:54
 * @description: netty连接类
 * 1.Netty Client启动的时候需要重连
 * 2.在程序运行中连接断掉需要重连。
 * 使用object关键字替代class关键字就可以声明一个单例对象
 */
object NettyConnect {
    //companion静态声类声名对象,相当于static关键
    //companion object { // 自定义委托实现单例,只能修改这个值一次.
    //如果要发送数据直接调用NettyConnect.channel?.writeAndFlush(""); 或着 导包直接使用channel.writeAndFlush("")
    var channel: Channel by DelegatesExt.notNullSingleValue<Channel>();
    /*    NioEventLoopGroup可以理解为一个线程池,内部维护了一组线程,
       每个线程负责处理多个Channel上的事件,而一个Channel只对应于一个线程,这样可以回避多线程下的数据同步问题。*/
    private var eventLoopGroup: EventLoopGroup? = null;//=NioEventLoopGroup()
    /* Bootstrap是开发netty客户端的基础,通过Bootstrap的connect方法来连接服务器端。该方法返回的也是ChannelFuture,
        通过这个我们可以判断客户端是否连接成功,*/
    private var bootstrap: Bootstrap? = null;
    //使用Thread线程进行异步连接
    private var mThread: Thread? = null;
    //ChannelFuture的作用是用来保存Channel异步操作的结果。
    private var future: ChannelFuture? = null;
    //保存连接成功或着失败
    private var onDestrYN: Boolean = false;
    //   }

    //创建连接方法,如果要调用连接,直接调用此方法即可( NettyConnect.reConnect() )
    fun nettyConnect() {

        mThread = object : Thread("NettyConnect.reConnect") {
            override fun run() {
                try {
                    // super.run();
                    eventLoopGroup = NioEventLoopGroup(2);
                    bootstrap = Bootstrap()
                    bootstrap!!
                            .group(eventLoopGroup)
                            .channel(NioSocketChannel::class.java)
                            .option(ChannelOption.TCP_NODELAY, true)
                            //// 设置TCP连接超时时间 10.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000 * 10)//设置连接超时时间
                            .option(ChannelOption.SO_KEEPALIVE, true)
                            //.remoteAddress(host, port)
                            .handler(object : ChannelInitializer<SocketChannel>() {
                                @kotlin.jvm.Throws(java.lang.Exception::class)
                                override fun initChannel(ch: SocketChannel) {
                                    var pipeline: ChannelPipeline = ch.pipeline();
                                    /* 使用pipeline.addLast()添加,Decoder、Encode和Handler对象
                                      Decoder接收数据,Encode发送数据,Handler是编码和解码工具*/
                                    //解析编码为UTF-8
                                    pipeline.addLast(StringDecoder(CharsetUtil.UTF_8));
                                    //打包编码为UTF-8
                                    pipeline.addLast(StringEncoder(CharsetUtil.UTF_8));

                                    // 添加长度字段解码器
                                    // 在MessagePack解码器之前增加LengthFieldBasedFrameDecoder,用于处理半包消息
                                    // 它会解析消息头部的长度字段信息,这样后面的MsgpackDecoder接收到的永远是整包消息
                                    ch.pipeline().addLast("frameDecoder",
                                            LengthFieldBasedFrameDecoder(65535, 0,
                                                    2, 0, 2))
                                    // 添加MesspagePack解码器
                                    ch.pipeline().addLast("msgpack decoder", DecodeHandler());//接收服务器返回的数据包解析
                                    // 添加长度字段编码器
                                    // 在MessagePack编码器之前增加LengthFieldPrepender,它将在ByteBuf之前增加2个字节的消息长度字段
                                    ch.pipeline().addLast("frameEncoder", LengthFieldPrepender(2))
                                    // 添加MessagePack编码器
                                    ch.pipeline().addLast("msgpack encoder", EncodeHandler());//发送前打包数据并编码
                                    // 添加业务处理handler
                                    ch.pipeline().addLast(MsgHandler());//接收返回数据

                                }
                            })
                    LogUtils.d("NettyConnect",
                            "正在连接服务器 ipStr=${COMMAND_IP},portInt=${COMMAND_PORT}");
                    //ChannelFuture的作用是用来保存Channel异步操作的结果。
                    //不使用监听
                    // val future: ChannelFuture = bootstrap!!.connect(FinalValue.COMMAND_IP,FinalValue.COMMAND_PORT).sync();
                    //使用监听,监听是否连接或是断开
                    // val future: ChannelFuture = bootstrap!!.connect(FinalValue.COMMAND_IP,FinalValue.COMMAND_PORT);
                    //{addListener(GenericFutureListener)}的方式来获得通知,而非await()。使用sync异步执行
                    future = bootstrap!!.connect(COMMAND_IP, COMMAND_PORT)!!.sync();//// 发起异步连接操作
                    //如果连接成功则保存ChannelFuture到Channel
//                        channel = future!!.awaitUninterruptibly().channel();
                    if (future!!.isSuccess) {
                        //如果连接成功则保存ChannelFuture到Channel
                        channel = future!!.awaitUninterruptibly().channel();
                        channel.closeFuture().sync();// 等待客户端链路关闭
                        // 优雅退出,释放NIO线程组
                        eventLoopGroup!!.shutdownGracefully();
//                            //如果连接成功则保存ChannelFuture到Channel
//                            channel = future!!.channel() as Channel
                        LogUtils.d("NettyConnect", "服务器连接成功ipStr=${COMMAND_IP},portInt=${COMMAND_PORT}")
                        onDestrYN = true;//连接成功
                    } else {
                        onDestrYN = false;//连接失败
                        while (onDestrYN) {//连接失败一直进行连接,不管是什么原因都进行连接
                            LogUtils.d("NettyConnect", "连接失败再次连接")
                            //断开连接,重新进行连接
                            channel!!.disconnect();
                            channel!!.close();
                            future!!.channel().eventLoop().schedule(
                                    {
                                        //重新开新的线程进行连接
                                        if (future!!.isSuccess) {
                                            //如果连接成功则保存ChannelFuture到Channel
                                            channel = future!!.channel() as Channel;
                                            //channel = future!!.awaitUninterruptibly().channel()
                                            onDestrYN = true;//连接成功
                                        };
                                    }, 10,//2秒重新连接
                                    TimeUnit.SECONDS);
                        };
                    };
                } catch (ex: Exception) {
                    LogUtils.d("NettyConnect", "连接出现异常重新连接${ex.toString()}");
                    executor.execute {
                        try {
                            TimeUnit.SECONDS.sleep(1);
                            nettyDestroy();
                            nettyConnect();// 发起重连操作
                        } catch (e: InterruptedException) {
                            e.printStackTrace();
                        };
                    };
                };
            };
        };

        mThread!!.start();
    }

    private val executor = Executors.newScheduledThreadPool(1)
    //? 表示当前对象是否可以为空
    //!! 表示当前对象不为空的情况下执行
    fun nettyDestroy() {
        // channel = null;
        //  Bootstrap
        bootstrap = null;
        //结束线程池
        if (eventLoopGroup != null) {
            // 优雅退出,释放NIO线程组
            eventLoopGroup!!.shutdownGracefully();
        }
        eventLoopGroup = null;
        //结束线程
        mThread!!.interrupt();
        mThread!!.join();
        mThread = null;
        //结束连接
        if (future != null && future!!.isSuccess) {
            /* 当对应的channel关闭的时候,就会返回对应的channel。
                       Returns the ChannelFuture which will be notified when this channel is closed.
                       This method always returns the same future instance.*/
            try {
                channel!!.closeFuture().sync();
                channel!!.flush();
            } catch (e: Exception) {
                e.printStackTrace()
            }
            channel!!.close();
        }

        //ChannelFuture,结束监听
        //future?.removeListener { }
        future = null;
    }
}

kotlin 项目中 Netty中使用MessagePack 传输 编码/解码

猜你喜欢

转载自blog.csdn.net/jeff_yaojie/article/details/80341457