0%

编解码器

编解码器

数据在网络中传输时都是二进制字节码数据,在发送数据时需要编码,接收数据时需要解码,所以就需要解码器(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>
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<!-- netty5不在维护 -->
<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>
<!-- 指定Protobuf编译器protoc的版本 --><protocArtifact>com.google.protobuf:protoc:3.11.3:exe:${os.detected.classifier}</protocArtifact>
<!-- 指定protoc的插件id -->
<pluginId>grpc-java</pluginId>

<!-- 指定生成Java代码的具体插件版本,用于生成Java接口服务 -->
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.32.1:exe:${os.detected.classifier}</pluginArtifact>
<!-- proto文件所在目录 -->
<protoSourceRoot>src/main/proto</protoSourceRoot>
<!-- Java类输出目录 -->
<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"; // 生成的外部java类名

message User {// 在UserProto类中生成一个内部类User,真正的数据对象
int32 id = 1; // 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 {
// 客户端处理器
// 配置ProtobufEncoder是proto编码器
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
// 辅助类  对server进行一系列的配置
ServerBootstrap bootstrap = new ServerBootstrap();
// 将两个工作线程组加入进来
bootstrap.group(bossGroup,workerGroup)
// 指定NioServerSocketChannel类型的通道
.channel(NioServerSocketChannel.class)
// 使用childHandler绑定具体的事件处理器
.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());
}
})
// tcp缓冲区,线程队列等待连接的个数
.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

欢迎关注我的其它发布渠道