netty编码与解码
1、编写网络应用程序时,因为数据在网络中传输的都是二进制字节码数据,在发送数据时就需要编码,接收数据时就需要解码 ,codec(编解码器) 的组成部分有两个:decoder(解码器)和 encoder(编码器)。encoder 负责把业务数据转换成字节码数据,decoder 负责把字节码数据转换成业务数据。
2、netty提供的编码器和解码器有:
StringEncoder,对字符串数据进行编码 ObjectEncoder,对 Java 对象进行编码 StringDecoder, 对字符串数据进行解码 ObjectDecoder,对 Java 对象进行解码 MessageToByteEncoder 编码器,这个解码器是一个抽象类,需要实现它提供的方法来完成自己的业务需求。 ByteToMessageDecoder 解码器,这个解码器是一个抽象类,需要实现它提供的方法来完成自己的业务需求。 ReplayingDecoder<s> 解码器, 参数S指定了用户状态管理的类型,其中Void代表不需要状态管理 LineBasedFrameDecoder:这个类在Netty内部也有使用,它使用行尾控制字符(\n或者\r\n)作为分隔符来解析数据。 |
3、使用自定义编码器来进行编码和解码处理消息:
解码器1: public class MyByteToLongDecoder extends ByteToMessageDecoder { /** * 解码器的方法 * @param channelHandlerContext * @param byteBuf * @param list * @throws Exception */ @Override protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { System.out.println("MyByteToLongDecoder解码器被调用..."); if(byteBuf.readableBytes() > 8){ list.add(byteBuf.readLong()); } } } 解码器2: public class MyByteToLongDecoder2 extends ReplayingDecoder<Void>{ /** * 街按摩器方法 * @param channelHandlerContext * @param byteBuf * @param list * @throws Exception */ @Override protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception { //在 ReplayingDecoder 不需要判断数据是否足够读取,内部会进行处理判断 System.out.println("MyByteToLongDecoder2被调用..."); list.add(byteBuf.readLong()); } } |
编码器: public class MyLongToByteEncoder extends MessageToByteEncoder<Long> { /** * 编码器处理的方法 * @param channelHandlerContext 上下文对象 * @param msg 传送过来的数据 * @param byteBuf * @throws Exception */ @Override protected void encode(ChannelHandlerContext channelHandlerContext, Long msg, ByteBuf byteBuf) throws Exception { System.out.println("MyLongToByteEncoder encoder 编码器被调用"); System.out.println("接收到的消息:" + msg); //将数据写入到缓冲区 byteBuf.writeLong(msg); } } |
4、使用谷歌的Protobuf来完成编解码器:
Protobuf 是 Google 发布的开源项目,全称 Google Protocol Buffers,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC[远程过程调用 remote procedure call ] 数据交换格式 。
Protobuf基本介绍和使用示意图
Protobuf高性能,高可靠性
使用 protobuf 编译器能自动生成代码,Protobuf 是将类的定义使用.proto 文件进行描述。
然后通过 protoc.exe 编译器根据.proto 自动生成.java 文件Protobuf基本介绍和使用示意图
1、编写MyTemplate.proto文件, //实体类模板,可以有多个类型 syntax = "proto3"; option optimize_for = SPEED;//加快解析 option java_package="com.yu.netty.protobufcodec";//指定生成在哪个包下 option java_outer_classname="MyTemplate"; //外部类名,文件名 //protobuf可以使用message管理其他的message message MyTemplateData { //定义一个枚举类型 enum DataType { StudentType = 0; //在proto3要求enum从0开始 TeacherType = 1; } //使用data_type来标识传的是哪一个枚举类型 DataType data_type = 1; //表示每次枚举最多只能出现其中的一个 oneof dataBody { Student student = 2; Teacher teacher = 3; } } //定义学生的信息 message Student { int32 id = 1; string name = 2; } //定义老师的信息 message Teacher { int32 id = 1; string name = 2; int32 age = 3; } 2、通过protoc.exe文件来生成对应的java文件,命令为:protoc.exe --java_out=. MyTemplate.proto 3、编写对应的客户端和服务器: 服务端代码: public class ProtoServer { public static void main(String[] args) throws Exception{ NioEventLoopGroup bossGroup = new NioEventLoopGroup(1); NioEventLoopGroup workGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG,128) .childOption(ChannelOption.SO_KEEPALIVE,true) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); //添加解码器 pipeline.addLast(new ProtobufDecoder(MyTemplate.MyTemplateData.getDefaultInstance())); //添加自定义的handler pipeline.addLast(new ProtobufHandler()); } }); System.out.println("客户端isok"); ChannelFuture channelFuture = serverBootstrap.bind(6666).sync(); channelFuture.channel().closeFuture().sync(); }finally { workGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } } 服务端对于的handler: public class ProtobufHandler extends SimpleChannelInboundHandler<MyTemplate.MyTemplateData> { /** * 读取数据信息 * @param channelHandlerContext * @param myTemplateData * @throws Exception */ @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyTemplate.MyTemplateData myTemplateData) throws Exception { MyTemplate.MyTemplateData.DataType dataType = myTemplateData.getDataType(); if(dataType == MyTemplate.MyTemplateData.DataType.StudentType){ //输出的是学生信息 MyTemplate.Student student = myTemplateData.getStudent(); System.out.println("学生的信息为:id=" + student.getId() + ": name=" + student.getName()); }else if(dataType == MyTemplate.MyTemplateData.DataType.TeacherType){ //输出的是老师信息 MyTemplate.Teacher teacher = myTemplateData.getTeacher(); System.out.println("老师的信息为:id=" + teacher.getId() + ": name=" + teacher.getName() + ":age=" + teacher.getAge()); }else{ System.out.println("传入的数据有误...."); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } } 客户端代码: public class ProtoClient { public static void main(String[] args) throws Exception{ NioEventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); //添加编码器 pipeline.addLast(new ProtobufEncoder()); pipeline.addLast(new ProtoClientHandler()); } }); System.out.println("服务端isok"); ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync(); channelFuture.channel().closeFuture().sync(); }finally { group.shutdownGracefully(); } } } 客户端对于的handler: public class ProtoClientHandler extends SimpleChannelInboundHandler<MyTemplate.MyTemplateData> { /** * 发送数据信息 * @param ctx * @throws Exception */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { int random = new Random().nextInt(3); MyTemplate.MyTemplateData templateData = null; if(random == 0){ templateData = MyTemplate.MyTemplateData.newBuilder() .setDataType(MyTemplate.MyTemplateData.DataType.TeacherType) .setTeacher(MyTemplate.Teacher.newBuilder().setAge(20).setId(1).setName("王老师")).build(); }else{ templateData = MyTemplate.MyTemplateData.newBuilder() .setDataType(MyTemplate.MyTemplateData.DataType.StudentType) .setStudent(MyTemplate.Student.newBuilder().setId(1).setName("张三")).build(); } ctx.writeAndFlush(templateData); } @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, MyTemplate.MyTemplateData myTemplateData) throws Exception { } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
|
本文地址:https://blog.csdn.net/qq_27293643/article/details/107673630
上一篇: Java四种访问控制修饰符知识点总结
下一篇: 实例分析Java泛型