编解码器
数据在网络中传输时都是二进制字节码数据,在发送数据时需要编码,接收数据时需要解码,所以就需要解码器(decoder)和编码器(encoder)来完成该操作
encoder编码器是将指定的消息对象转为二进制流,负责处理出站数据;decoder解码器是将二进制流转为指定的消息对象,负责处理入站数据
Netty本身提供了一些编解码器
- StringEncoder、StringDecoder 对字符串数据进行编解码
- ObjectEncoder、ObjectDecoder 对java对象进行编解码
Netty本身提供的ObjectEncoder、ObjectDecoder 底层使用的是Java序列化技术,而Java序列化技术本身效率不高,序列化后太大,且无法跨语言,所以可以使用Protobuf
编解码器本身也是ChannelHandler
Protobuf
是一种轻便高效的结构化数据存储格式,可以用于数据序列化,支持跨平台,跨语言
由于我是使用的java语言,所以需要将proto转成java,不想去下载proto环境,就直接使用maven来进行生成吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| <dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.57.Final</version> </dependency>
<dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.11.3</version> </dependency> </dependencies>
<build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.5.0.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.6.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.11.3:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.32.1:exe:${os.detected.classifier}</pluginArtifact> <protoSourceRoot>src/main/proto</protoSourceRoot> <outputDirectory>src/main/java</outputDirectory> <clearOutputDirectory>false</clearOutputDirectory> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
|
proto对象类
1 2 3 4 5 6 7 8
| syntax = "proto3"; package com.zhanghe.study.netty.codec; option java_outer_classname = "UserProto";
message User { int32 id = 1; string name = 2; }
|
使用maven编译之后会生成对应的java类UserProto
也可以使用命令来将proto转为java,
1
| protoc --java_out=../java ./user.proto
|
使用编解码器
客户端
配置编解码器
1 2 3 4 5 6 7 8 9 10 11 12 13
| Bootstrap bootstrap = new Bootstrap(); bootstrap.group(group) .channel(NioSocketChannel.class) // 客户端NioSocketChannel通道 .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline() .addLast("encoder",new ProtobufEncoder()) .addLast(new ClientHandler()); } });
|
发送消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class ClientHandler extends ChannelInboundHandlerAdapter {
@Override public void channelActive(ChannelHandlerContext ctx) throws Exception { UserProto.User user = UserProto.User.newBuilder().setId(10).setName("张三").build(); ctx.writeAndFlush(user); }
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try{ ByteBuf buf = (ByteBuf) msg; byte[] data = new byte[buf.readableBytes()]; buf.readBytes(data); String request = new String(data, StandardCharsets.UTF_8); System.out.println("接收到的回复为:->"+request);
} finally { ReferenceCountUtil.release(msg); } }
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
|
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { UserProto.User defaultInstance = UserProto.User.getDefaultInstance(); socketChannel.pipeline() .addLast("decoder",new ProtobufDecoder(defaultInstance)) .addLast(new ServerHandler()); } }) .option(ChannelOption.SO_BACKLOG,128) .childOption(ChannelOption.SO_KEEPALIVE,true);
|
自定义编解码器
解码器
解码器分为两种方式
继承的是ChannelInboundHandlerAdapter
- 字节 to 消息 ByteToMessageDecoder
- 消息 to 消息 MessageToMessageDecoder
来重写decode方法
1
| protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
|
使用的时候将入站数据转换格式后传递给ChannelPipline中的下一个ChannelInboundHandler进行处理
编码器
编码器分为两种方式
继承的是ChannelOutboundHandlerAdapter
- 消息 to 消息 MessageToMessageEncoder
- 消息 to 字节 MessageToByteEncoder
重写encode方法
1
| protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
|
编解码器
既可编码又可解码
使用encode方法进行编码,使用decode方法进行解码
也可以分为两种方式
MessageToMessageCodec和ByteToMessageCodec
Http相关的编解码器
- HttpRequestEncoder 将HttpRequest或HttpContent编码为ByteBuf
- HttpRequestDecoder 将ByteBuf编码为HttpRequest和HttpContent
- HttpResponseEncoder 将HttpResponse或HttpContent编码为ByteBuf
- HttpResponseDecoder 将ByteBuf解码为HttpResponse和HttpContent