Android Netty的使用-进阶指南

Netty 编解码

Java原生的编解码 ①

简介

	java 序列化只需要实现 java.io.Serializable 接口即可 但是有如下缺点
	1,无法跨语言
	2,序列化后码流太大

发送类 和 响应类

SubscribeResp

/**
 * Netty 中 对 POJO 对象 进行Java 序列化
 *
 * 订购响应 应答 POJO类 定义
 */
public class SubscribeResp implements Serializable {
    //默认序列号Id
    private static  final long serialVersionUID=3L;
    private int subReqID;
    private int respCode;
    private String desc;

    @Override
    public String toString() {
        return "SubscribeResp[" +
                "subReqID=" + subReqID +
                ", respCode=" + respCode +
                ", desc='" + desc + '\'' +
                ']';
    }

  get set ...
}

SubscribeReq

/**
 * Netty 中 对 POJO 对象 进行Java 序列化
 *
 * 订购请求POJO类 定义
 */
public class SubscribeReq  implements Serializable {
    //默认序列号Id
    private static  final long serialVersionUID=3L;
    private int subReqID;
    private String userName;
    private String productName;
    private String phoneNumber;
    private String address;

    @Override
    public String toString() {
        return "SubscribeReq[" +
                "subReqID=" + subReqID +
                ", userName='" + userName + '\'' +
                ", productName='" + productName + '\'' +
                ", phoneNumber='" + phoneNumber + '\'' +
                ", address='" + address + '\'' +
                ']';
    }
    get set ...
        }
    

Protobuf的编解码 ②

简介

Protobuf全称Google Protocol Buffers,它由谷歌开源而来,在谷歌内部久经考验。它将数据结构以.proto文件进行描述,通过代码生成工具可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性。它的特点如下。
1,结构化数据存储格式(XML, JSON等);
2,高效的编解码性能:
3,语言无关、平台无关、扩展性好;。
官方支持Java,C++和Python三种语言。首先我们来看下为什么不使用XML,尽管XML的可读性和可扩展性非常好,也非常适合描述数据结构,但是XML解析的时间开销和XML为了可读性而牺牲的空间开销都非常大,因此不适合做高性能的通信协议。Protobuf使用二进制编码,在空间和性能上具有更大的优势。

使用

这里需要把实体类转换成protobuf类
转换方式 看上一篇文章Protobuf的生成

JBoss Marshalling的编解码 ③

简介

JBoss Marshalling是一个Java对象的序列化API包,修正了JDK自带的序列化包的很多问题,但又保持跟java.io.Serializable接口的兼容;同时增加了一些可调的参数和附加的特性,并且这些参数和特性可通过工厂类进行配置。相比于传统的Java序列化机制,它的优点如下:
1 可插拔的类解析器,提供更加便捷的类加载定制策略,通过一个接口即可实现定制:
2 可插拔的对象替换技术,不需要通过继承的方式;
3 可插拔的预定义类缓存表,可以减小序列化的字节数组长度,提升常用类型的对象序列化性能; 无须实现java.io.Serializable接口,即可实现Java序列化;通过缓存技术提升对象的序列化性能。

相比于前面介绍的两种编解码框架, JBoss Marshalling更多是在JBoss内部使用,应用范围有限。

使用

直接导入jar 即可 ,和原生的一样
Marshalling编解码实体类和原生JAVA的是一样的

import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;

public class MarshallingCodeCFactory {
    /**
     * 创建 Jboss Marshalling解码器
     */
    public static MarshallingDecoder buildMarshallingDecoder() {
        //serial 表示创建的是java 序列化工厂对象
        final MarshallerFactory marshallerFactory = Marshalling.getMarshallerFactory("serial");
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        //版本号
        configuration.setVersion(5);
        UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
        MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024);
        return decoder;
    }

    /**
     * 创建 Jboss Marshalling 编码器
     */
    public static MarshallingEncoder buildMarshallingEncoder() {
        final MarshallerFactory marshallerFactory = Marshalling.getMarshallerFactory("serial");
        final MarshallingConfiguration configuration = new MarshallingConfiguration();
        configuration.setVersion(5);
        //用于将序列化接口pojo对象转成二进制
        MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
        MarshallingEncoder encoder = new MarshallingEncoder(provider);
        return encoder;
    }

}

这里 上述三种实现方式 都汇集在一起了。 具体看 ① ② ③ 注释`

服务端

SubReqServer

/**
 * 服务端主函数
 */
public class SubReqServer {
    public void bind(int port) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 100)
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //①Netty 自带的 解码 编码 负责实现对序列化的POJO对象进行解码,
//                            socketChannel.pipeline().addLast(new ObjectDecoder(1024 * 1024, ClassResolvers.weakCachingConcurrentResolver(this.getClass().getClassLoader())));
//                            //在消息发送的时候自动将实现的pojo对象进行编码
//                            socketChannel.pipeline().addLast(new ObjectEncoder());


                            //②Protobuf 解码 编码-------
                            //ProtobufVarint32FrameDecoder:半包处理
//                            socketChannel.pipeline().addLast(new ProtobufVarint32FrameDecoder());
//                            //告诉ProtobufDecoder 需要解码的目标类 似否则仅仅从字节数组中是无法判断要解码的目标类型//todo : 如果有多个目标。。咋办?
//                            socketChannel.pipeline().addLast(new ProtobufDecoder(SubscribeReqProto.SubscribeReq.getDefaultInstance()));
//                            socketChannel.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());
//                            socketChannel.pipeline().addLast(new ProtobufEncoder());

                            //③ 使用Marshalling
                            socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
                            socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());

                            socketChannel.pipeline().addLast(new SubReqServerHandler());
                        }
                    });
            //绑定端口,同步等待成功
            ChannelFuture f = b.bind(port).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //优雅退出
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }
}

SubReqServerHandler


public class SubReqServerHandler extends ChannelHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //已经被自动解码成了 SubscribeReq
        //②Protobuf 解码 编码-------
//        SubscribeReqProto.SubscribeReq req = (SubscribeReqProto.SubscribeReq) msg;
        System.out.println("channelActive:=================111========");

//      ① Netty ③ Marshalling
       SubscribeReq req = (SubscribeReq) msg;
        System.out.println("channelActive:========================="+req.getUserName());

        //校验信息是否准确
        if ("Joy Chen".equalsIgnoreCase(req.getUserName())) {
            System.out.println("服务连接客户端序列化对象:[" + req.toString() + "]");
            //消息 回馈给 客户端
            if (req.getSubReqID() % 2 == 0) {
                //①  ③JDK自带的 解码 编码 负责实现对序列化的POJO对象进行解码,
                ctx.writeAndFlush(resp(req.getSubReqID(), "购买成功 ,3天后发送到你的地址"));

                //②Protobuf 解码 编码-------
//                ctx.writeAndFlush(resp(req.getSubReqID()));
            } else {
                //① ③jdk自带的 解码 编码 负责实现对序列化的POJO对象进行解码,
                ctx.writeAndFlush(resp(req.getSubReqID(), "购买失败 ,不给你"));

                //②Protobuf 解码 编码-------
//                ctx.writeAndFlush(resp(req.getSubReqID()));

            }
        }

    }
    //②Protobuf 解码 编码-------
    private SubscribeRespProto.SubscribeResp resp(int subReqId) {
        SubscribeRespProto.SubscribeResp.Builder builder = SubscribeRespProto.SubscribeResp.newBuilder();
        builder.setSubReqID(subReqId);
        builder.setRespCode(0);
        builder.setDesc("购买成功三天后发给你");
        return builder.build();

    }
    //① ③ 
    public SubscribeResp resp(int subReqID, String msg) {
        SubscribeResp resp = new SubscribeResp();
        resp.setRespCode(0);
        resp.setSubReqID(subReqID);
        resp.setDesc(msg);
        return resp;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();//发生异常关闭
    }
}

客户端

SubReqClientHandler

/**
 *  这里实体类 前后端的路径 务必保持一致
 */
class SubReqClientHandler : ChannelHandlerAdapter() {
    override fun channelActive(ctx: ChannelHandlerContext?) {
        println("channelActive:=========================")
        for (i in 0..10) {
            //① ③netty 自带 发送数据给 服务端

            ctx!!.write(subReq(i))

            //② Protobuf发送数据给服务端
//            ctx!!.write(createSubscribeReq(i))
        }
        ctx!!.flush()
    }

    //① ③ 模拟请求数据
    fun subReq(id: Int): SubscribeReq {
        var req = SubscribeReq()
        req.userName = "Joy Chen"
        req.phoneNumber = "18806068888"
        req.productName = "Netty 权威指南"
        req.subReqID = id
        req.address = "深圳市大梅沙海滨公园"
        return req
    }

    override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
        Log.e("CWJ-", "channelRead 注册服务响应:【${msg.toString()}】")
    }

    //② 模拟请求数据
    private fun createSubscribeReq(i:Int): SubscribeReqProto.SubscribeReq? {
        val builder = SubscribeReqProto.SubscribeReq.newBuilder()
        builder.subReqID = i
        builder.userName = "Joy Chen"
        builder.productName = "netty book"
        val address: MutableList<String> = ArrayList()
        address.add("NanJing")
        address.add("BeiJing")
        address.add("ShenZhen")
        builder.addAllAddress(address)
        return builder.build()
    }

    override fun channelReadComplete(ctx: ChannelHandlerContext?) {
        ctx!!.flush()
    }

    override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
        cause!!.printStackTrace()
        ctx!!.close()
    }
       }
/**
 * ProtobufDecoder仅仅负责解码,它不支持读半包。因此,在ProtobufDecoder前面,一定要有能够处理读半包的解码器,有三种方式可以选择。
 * , 使用Netty提供的ProtobufVarint32FrameDecoder,它可以处理半包消息;
 * 3 继承Netty提供的通用半包解码器LengthFieldBasedFrameDecoder;
 * 继承ByteToMessageDecoder类, 自己处理半包消息。
 */


class SubReqClient {
    fun connet(port: Int, host: String) {
        //配置 客户端 NIO线程组
        var group: EventLoopGroup = NioEventLoopGroup();

        try {
            var b = Bootstrap()
            b.group(group).channel(NioSocketChannel::class.java)
                .option(ChannelOption.TCP_NODELAY, true)
                .handler(object : ChannelInitializer<SocketChannel>() {
                    override fun initChannel(ch: SocketChannel?) {
                        //① Netty自带的 解码 编码 负责实现对序列化的POJO对象进行解码,
                        //  禁止对类加载器进行缓存
//                        ch!!.pipeline().addLast(
//                            ObjectDecoder(
//                                1024,
//                                ClassResolvers.cacheDisabled(this.javaClass.classLoader)
//                            )
//                        )
//                        ch!!.pipeline().addLast(ObjectEncoder())


                        //②Protobuf 方式 解码 编码-------
                        //ProtobufVarint32FrameDecoder:半包处理
//                        ch!!.pipeline().addLast(ProtobufVarint32FrameDecoder())
                        //告诉ProtobufDecoder 需要解码的目标类 似否则仅仅从字节数组中是无法判断要解码的目标类型//todo : 如果有多个目标。。咋办?
//                        ch!!.pipeline()
//                            .addLast(ProtobufDecoder(SubscribeRespProto.SubscribeResp.getDefaultInstance()))
//                        ch!!.pipeline().addLast(ProtobufVarint32LengthFieldPrepender())
//                        ch!!.pipeline().addLast(ProtobufEncoder())

                        //③Marshalling 方式
                        ch!!.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder())
                        ch!!.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder())




                        ch!!.pipeline().addLast(SubReqClientHandler())
                    }

                })
            //发起异步连接操作
            var f = b.connect(host, port).sync()
            //等待客户端链路关闭
            f.channel().closeFuture().sync()
        } finally {
            //优雅退出,释放线程组
            group.shutdownGracefully()
        }

    }
}

备注

ProtobufDecoder仅仅负责解码,它不支持读半包。因此,在ProtobufDecoder前面,一定要有能够处理读半包的解码器,有三种方式可以选择。

  • , 使用Netty提供的ProtobufVarint32FrameDecoder,它可以处理半包消息;
  • 3 继承Netty提供的通用半包解码器LengthFieldBasedFrameDecoder;
  • 继承ByteToMessageDecoder类, 自己处理半包消息。
    SubReqClient

结尾

参考《Netty权威指南》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值