1.概念
Netty提供了一系列实用的编码解码器,他们都实现了ChannelInboundHadnler或者ChannelOutcoundHandler接口。在这些类中,channelRead方法已经被重写了。以入站为例,对于每个从入站Channel读取的消息,这个方法会被调用。随后,它将调用由已知解码器所提供的decode方法进行解码,并将已经解码的字节转发给ChannelPipeline中的下一个ChannelInboundHandler。
2.解码器(Decoder)
对于解码器,Netty中主要提供了抽象基类ByteToMessageDecoder和MessageToMessageDecoder
2.1 抽象类ByteToMessageDecoder
用于将接收到的二进制数据(Byte)解码,得到完整的请求报文(Message)。
ByteToMessageDecoder提供的一些常见的实现类:
- FixedLengthFrameDecoder:定长协议解码器,我们可以指定固定的字节数算一个完整的报文
- LineBasedFrameDecoder:行分隔符解码器,遇到\n或者\r\n,则认为是一个完整的报文
- DelimiterBasedFrameDecoder:分隔符解码器,与LineBasedFrameDecoder类似,只不过分隔符可以自己指定
- LengthFieldBasedFrameDecoder:长度编码解码器,将报文划分为报文头/报文体,根据报文头中的Length字段确定报文体的长度,因此报文提的长度是可变的
- JsonObjectDecoder:json格式解码器,当检测到匹配数量的"{" 、”}”或”[””]”时,则认为是一个完整的json对象或者json数组。
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
2.2 抽象类MessageToMessageDecoder
ByteToMessageDecoder是将二进制流进行解码后,得到有效报文。而MessageToMessageDecoder则是将一个本身就包含完整报文信息的对象转换成另一个Java对象。
/**
* 参数msg,需要进行解码的参数。例如ByteToMessageDecoder解码后的得到的包含完整报文信息ByteBuf
* List<Object> out参数:将msg经过解析后得到的java对象,添加到放到List<Object> out中
*/
protected abstract void decode(ChannelHandlerContext ctx, I msg, List<Object> out) throws Exception;
3.编码器
Netty提供了对应的编码器实现MessageToByteEncoder和MessageToMessageEncoder,二者都实ChannelOutboundHandler接口。
4.编码解码器Codec
编码解码器同时具有编码与解码功能,特点同时实现了ChannelInboundHandler和ChannelOutboundHandler接口,因此在数据输入和输出时都能进行处理。Netty提供提供了一个ChannelDuplexHandler适配器类,编码解码器的抽象基类 ByteToMessageCodec 、MessageToMessageCodec都继承与此类,如下
ByteToMessageCodec内部维护了一个ByteToMessageDecoder和一个MessageToByteEncoder实例,可以认为是二者的功集合,泛型参数I是接受的编码类型:
public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler {
private final TypeParameterMatcher outboundMsgMatcher;
private final MessageToByteEncoder<I> encoder;
private final ByteToMessageDecoder decoder = new ByteToMessageDecoder(){…}
...
protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception;
...
}
MessageToMessageCodec内部维护了一个MessageToMessageDecoder和一个MessageToMessageEncoder实例,可以认为是二者的功集合,泛型参数INBOUND_IN和OUTBOUND_IN分别表示需要解码和编码的数据类型。
public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelDuplexHandler {
private final MessageToMessageEncoder<Object> encoder= ...
private final MessageToMessageDecoder<Object> decoder =…
...
protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, List<Object> out) throws Exception;
protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, List<Object> out) throws Exception;
}
5.示例
从客户端发送字母A,经过解码器ByteToMessageDecoder、ReplayingDecoder、MessageToMessageDecoder以及编码器MessageToMessageEncoder、MessageToByteEncoder,把A解码为a、b、c,再编码成d、e
EchoCodeServer
public class EchoCodeServer {
public static void main(String[] args) throws InterruptedException {
// 创建NioEventLoopGroup类型的EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();
try {
// 创建ServerBootstrap
ServerBootstrap sbs = new ServerBootstrap();
sbs.group(group)
// 设置Channel为NIO的服务端Channel
.channel(NioServerSocketChannel.class)
// 新连接被接受时,会创建一个Channel
// 再把把echoServerHandler加入到这个Channel的ChannelPipeline中
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new MyMessageToByteEncoder());
socketChannel.pipeline().addLast(new MyMessageToMessageEncoder());
socketChannel.pipeline().addLast(new MyByteToMessageDecoder());
socketChannel.pipeline().addLast(new MyReplayingDecoder());
socketChannel.pipeline().addLast(new MyMessageToMessageDecoder());
}
});
// 异步绑定服务器,阻塞到服务器绑定完成
ChannelFuture sync = sbs.bind(9999).sync();
// 获取channel的closeFuture,阻塞到关闭
sync.channel().closeFuture().sync();
} finally {
// 优雅的关掉group并释放所有的资源
group.shutdownGracefully().sync();
}
}
}
MyByteToMessageDecoder
public class MyByteToMessageDecoder extends ByteToMessageDecoder {
/**
* @param ctx
* @param in 传过来的ByteBuf
* @param out 添加解码消息的List
* @throws Exception
*/
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
String str = "";
str += (char) in.readByte();
System.out.println("MyByteToMessageDecoder receive:" + str);
str = "a";
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(str.getBytes());
out.add(byteBuf);
System.out.println("MyByteToMessageDecoder send:" + str);
}
}
MyReplayingDecoder
public class MyReplayingDecoder extends ReplayingDecoder<Void> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
String str = "";
str += (char) in.readByte();
System.out.println("MyReplayingDecoder receive:" + str);
str = "b";
out.add(str);
System.out.println("MyReplayingDecoder send:" + str);
}
}
MyMessageToMessageDecoder
public class MyMessageToMessageDecoder extends MessageToMessageDecoder<String> {
@Override
protected void decode(ChannelHandlerContext ctx, String msg, List out) throws Exception {
System.out.println("MyMessageToMessageDecoder receive:" + msg);
//out.add(msg);
msg = "c";
System.out.println("MyMessageToMessageDecoder send:" + msg);
ctx.write(msg);
}
}
MyMessageToMessageEncoder
public class MyMessageToMessageEncoder extends MessageToMessageEncoder<String> {
@Override
protected void encode(ChannelHandlerContext ctx, String msg, List out) throws Exception {
System.out.println("MyMessageToMessageEncoder receive:" + msg);
msg = "d";
System.out.println("MyMessageToMessageEncoder send:" + msg);
out.add(msg);
}
}
MyMessageToByteEncoder
public class MyMessageToByteEncoder extends MessageToByteEncoder<String> {
@Override
protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out) throws Exception {
System.out.println("MyMessageToByteEncoder receive:" + msg);
msg = "e";
System.out.println("MyMessageToByteEncoder send:" + msg);
out.writeBytes(msg.getBytes());
ctx.writeAndFlush(out);
}
}
EchoCodeClient
public class EchoCodeClient {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
// 创建Bootstrap
Bootstrap bs = new Bootstrap();
bs.group(group)
// 设置Channel为NIO的客户端Channel
.channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new MyClientHandler());
}
});
// 连接远程服务器,阻塞到连接完成
ChannelFuture cf = bs.connect("127.0.0.1", 9999).sync();
// 获取channel的closeFuture,阻塞到关闭
cf.channel().closeFuture().sync();
} finally {
// 优雅的关掉group并释放所有的资源
group.shutdownGracefully().sync();
}
}
}
MyClientHandler
public class MyClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
/**
* 客户端收到服务器消息后调用
*
* @param channelHandlerContext
* @param byteBuf
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
// 处理收到的消息
System.out.println("客户端收到信息:" + byteBuf.toString(CharsetUtil.UTF_8));
}
/**
* 客户端与服务器连接后调用
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// 当channel是活跃的时候,往服务端发送一条消息
ctx.writeAndFlush(Unpooled.copiedBuffer("A", CharsetUtil.UTF_8));
}
/**
* 客户端处理消息过程中,对异常的处理
*
* @param ctx
* @param cause
* @throws Exception
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}