什么是编解码器
如果将消息看作是对于特定的应用程序具有具体含义的结构化的字节序列—它的数据。那么编码器是将消息转换为适合于传输的格式(最有可能的就是字节流);而对应的解码器则是将网络字节流转换回应用程序的消息格式。因此,编码器操作出站数据,而解码器处理入站数据。
解码器
Netty涵盖两种类:
1.将字节解码为消息——ByteToMessageDecoder 和ReplayingDecoder;
2.将一种消息类型解码为另一种——MessageToMessageDecoder。
- 抽象类ByteToMessageDecoder
public class ToIntegerDecoder extends ByteToMessageDecoder { // 扩展ByteToMessageDecoder 类,以将字节解码为特定的格式
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in,List<Object> out) throws Exception {
if (in.readableBytes() >= 4) {
out.add(in.readInt());
}
}
}
注::一旦消息被编码或者解码,它就会被ReferenceCountUtil.release(message)调用自动释放。如果你需要保留引用以便稍后使用,那么你可以调用ReferenceCountUtil.retain(message)方法.
-
抽象类ReplayingDecoder
ReplayingDecoder扩展了ByteToMessageDecoder类不必调用readableBytes()方法。它通过使用一个自定义的ByteBuf实现,ReplayingDecoderByteBuf,包装传入的ByteBuf实现了这一点,其将在内部执行该调用.
public class ToIntegerDecoder2 extends ReplayingDecoder<Void> { // 扩展Replaying-Decoder<Void>以将字节解码为消息
@Override // 传入的ByteBuf 是ReplayingDecoderByteBuf
public void decode(ChannelHandlerContext ctx, ByteBuf in,List<Object> out) throws Exception {
out.add(in.readInt());
}
}
这里有一个简单的准则:如果使用ByteToMessageDecoder 不会引入太多的复杂性,那么请使用它;否则,请使用ReplayingDecoder。
-
抽象类MessageToMessageDecoder
定义IntegerToStringDecoder
public class IntegerToStringDecoder extends MessageToMessageDecoder<Integer> {
@Override
public void decode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
out.add(String.valueOf(msg));
}
}
- TooLongFrameException 类
public class SafeByteToMessageDecoder extends ByteToMessageDecoder {
private static final int MAX_FRAME_SIZE = 1024;
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) throws Exception {
int readable = in.readableBytes();
if (readable > MAX_FRAME_SIZE) { // 检查缓冲区中是否有超过MAX_FRAME_SIZE个字节
in.skipBytes(readable); // 跳过所有的可读字节,抛出TooLongFrameException 并通知ChannelHandler
throw new TooLongFrameException("Frame too big!");
}
// do something
...
}
}
以上为解码器的常规用例.
编码器
两种作用:
1.将消息编码为字节;
2.将消息编码为消息.
- 抽象类MessageToByteEncoder
public class ShortToByteEncoder extends MessageToByteEncoder<Short> {
@Override
public void encode(ChannelHandlerContext ctx, Short msg, ByteBuf out)throws Exception {
out.writeShort(msg);
}
}
Netty 提供了一些专门化的MessageToByteEncoder,你可以基于它们实现自己的编码器。WebSocket08FrameEncoder 类提供了一个很好的实例。你可以在io.netty.handler.codec.http.websocketx 包中找到它。
- 抽象类MessageToMessageEncoder
public class IntegerToStringEncoder extends MessageToMessageEncoder<Integer> {
@Override
public void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
out.add(String.valueOf(msg));
}
}
抽象的编解码器类
-
抽象类ByteToMessageCodec
ByteToMessageCodec 将为我们处理好这一切,因为它结合了ByteToMessageDecoder 以及它的逆向——MessageToByteEncoder
-
抽象类MessageToMessageCodec
public abstract class MessageToMessageCodec<INBOUND_IN,OUTBOUND_IN>
decode() 方法是将INBOUND_IN 类型的消息转换为OUTBOUND_IN 类型的消息, 而encode()方法则进行它的逆向操作。将INBOUND_IN类型的消息看作是通过网络发送的类型,而将OUTBOUND_IN类型的消息看作是应用程序所处理的类型,将可能有所裨益.
package com.hehe.netty.server;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.websocketx.*;
import java.util.List;
/**
* @author helong
* @date 2019/1/23 18:21
* @description
*/
public class WebSocketConvertHandler extends MessageToMessageCodec<WebSocketFrame, WebSocketConvertHandler.MyWebSocketFrame> {
@Override // 将MyWebSocketFrame 编码为指定的WebSocketFrame 子类型
protected void encode(ChannelHandlerContext ctx, WebSocketConvertHandler.MyWebSocketFrame msg, List<Object> out) throws Exception {
ByteBuf payload = msg.getData().duplicate().retain();
switch (msg.getType()) { // 实例化一个指定子类型的WebSocketFrame
case BINARY:
out.add(new BinaryWebSocketFrame(payload));
break;
case TEXT:
out.add(new TextWebSocketFrame(payload));
break;
case CLOSE:
out.add(new CloseWebSocketFrame(true, 0, payload));
break;
case CONTINUATION:
out.add(new ContinuationWebSocketFrame(payload));
break;
case PONG:
out.add(new PongWebSocketFrame(payload));
break;
case PING:
out.add(new PingWebSocketFrame(payload));
break;
default:
throw new IllegalStateException(
"Unsupported websocket msg " + msg);
}
}
@Override // 将WebSocketFrame 解码为MyWebSocketFrame,并设置FrameType
protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {
ByteBuf payload = msg.content().duplicate().retain();
if (msg instanceof BinaryWebSocketFrame) {
out.add(new MyWebSocketFrame(
MyWebSocketFrame.FrameType.BINARY, payload));
} else
if (msg instanceof CloseWebSocketFrame) {
out.add(new MyWebSocketFrame (
MyWebSocketFrame.FrameType.CLOSE, payload));
} else
if (msg instanceof PingWebSocketFrame) {
out.add(new MyWebSocketFrame (
MyWebSocketFrame.FrameType.PING, payload));
} else
if (msg instanceof PongWebSocketFrame) {
out.add(new MyWebSocketFrame (
MyWebSocketFrame.FrameType.PONG, payload));
} else
if (msg instanceof TextWebSocketFrame) {
out.add(new MyWebSocketFrame (
MyWebSocketFrame.FrameType.TEXT, payload));
} else
if (msg instanceof ContinuationWebSocketFrame) {
out.add(new MyWebSocketFrame (
MyWebSocketFrame.FrameType.CONTINUATION, payload));
} else
{
throw new IllegalStateException(
"Unsupported websocket msg " + msg);
}
}
public static final class MyWebSocketFrame {
public enum FrameType {
BINARY,
CLOSE,
PING,
PONG,
TEXT,
CONTINUATION
}
private final FrameType type;
private final ByteBuf data;
public MyWebSocketFrame(FrameType type, ByteBuf data) {
this.type = type;
this.data = data;
}
public FrameType getType() {
return type;
}
public ByteBuf getData() {
return data;
}
}
}
-
CombinedChannelDuplexHandler 类
结合一个解码器和编码器可能会对可重用性造成影响。但是,有一种方法既能够避免这种惩罚,又不会牺牲将一个解码器和一个编码器作为一个单独的单元部署所带来的便利性。CombinedChannelDuplexHandler 提供了这个解决方案,其声明为:
public class CombinedChannelDuplexHandler <I extends ChannelInboundHandler,O extends ChannelOutboundHandler>
public class ByteToCharDecoder extends ByteToMessageDecoder {
@Override
public void decode(ChannelHandlerContext ctx, ByteBuf in,
List<Object> out) throws Exception {
while (in.readableBytes() >= 2) {
out.add(in.readChar());
}
}
}
public class CharToByteEncoder extends MessageToByteEncoder<Character> {
@Override
public void encode(ChannelHandlerContext ctx, Character msg, ByteBuf out) throws Exception {
out.writeChar(msg);
}
}
public class CombinedByteCharCodec extends CombinedChannelDuplexHandler<ByteToCharDecoder, CharToByteEncoder> {
public CombinedByteCharCodec() {
super(new ByteToCharDecoder(), new CharToByteEncoder());
}
}
正如你所能看到的,在某些情况下,通过这种方式结合实现相对于使用编解码器类的方式来说可能更加的简单也更加的灵活。