http://blog.csdn.net/abc_key/article/details/38041143
本章介紹
- Codec,編解碼器
- Decoder,解碼器
- Encoder,編碼器
7.1 編解碼器Codec
- Decoder(解碼器)
- Encoder(編碼器)
7.2 解碼器
- 解碼字節到消息
- 解碼消息到消息
- 解碼消息到字節
7.2.1 ByteToMessageDecoder
- decode(ChannelHandlerContext, ByteBuf, List<Object>),這個方法是唯一的一個需要自己實現的抽象方法,作用是將ByteBuf數據解碼成其他形式的數據。
- decodeLast(ChannelHandlerContext, ByteBuf, List<Object>),實際上調用的是decode(...)。
/**
* Integer解碼器,ByteToMessageDecoder實現
* @author c.k
*
*/
public class ToIntegerDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if(in.readableBytes() >= 4){
out.add(in.readInt());
}
}
}
從上面的代碼可能會發現,我們需要檢查ByteBuf讀之前是否有足夠的字節,若沒有這個檢查豈不更好?是的,Netty提供了這樣的處理允許byte-to-message解碼,在下一節講解。除了ByteToMessageDecoder之外,Netty還提供了許多其他的解碼接口。7.2.2 ReplayingDecoder
ReplayingDecoder是byte-to-message解碼的一種特殊的抽象基類,讀取緩衝區的數據之前需要檢查緩衝區是否有足夠的字節,使用ReplayingDecoder就無需自己檢查;若ByteBuf中有足夠的字節,則會正常讀取;若沒有足夠的字節則會停止解碼。也正因爲這樣的包裝使得ReplayingDecoder帶有一定的侷限性。- 不是所有的操作都被ByteBuf支持,如果調用一個不支持的操作會拋出DecoderException。
- ByteBuf.readableBytes()大部分時間不會返回期望值
/**
* Integer解碼器,ReplayingDecoder實現
* @author c.k
*
*/
public class ToIntegerReplayingDecoder extends ReplayingDecoder<Void> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
out.add(in.readInt());
}
}
當從接收的數據ByteBuf讀取integer,若沒有足夠的字節可讀,decode(...)會停止解碼,若有足夠的字節可讀,則會讀取數據添加到List列表中。使用ReplayingDecoder或ByteToMessageDecoder是個人喜好的問題,Netty提供了這兩種實現,選擇哪一個都可以。7.2.3 MessageToMessageDecoder
/**
* 將接收的Integer消息轉成String類型,MessageToMessageDecoder實現
* @author c.k
*
*/
public class IntegerToStringDecoder extends MessageToMessageDecoder<Integer> {
@Override
protected void decode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
out.add(String.valueOf(msg));
}
}
7.2.4 解碼器總結
7.3 編碼器
- 消息對象編碼成消息對象
- 消息對象編碼成字節碼
7.3.1 MessageToByteEncoder
/**
* 編碼器,將Integer值編碼成byte[],MessageToByteEncoder實現
* @author c.k
*
*/
public class IntegerToByteEncoder extends MessageToByteEncoder<Integer> {
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, ByteBuf out) throws Exception {
out.writeInt(msg);
}
}
7.3.2 MessageToMessageEncoder
需要將消息編碼成其他的消息時可以使用Netty提供的MessageToMessageEncoder抽象類來實現。例如將Integer編碼成String,其工作流程如下圖:/**
* 編碼器,將Integer編碼成String,MessageToMessageEncoder實現
* @author c.k
*
*/
public class IntegerToStringEncoder extends MessageToMessageEncoder<Integer> {
@Override
protected void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {
out.add(String.valueOf(msg));
}
}
7.4 編解碼器
實際編碼中,一般會將編碼和解碼操作封裝太一個類中,解碼處理“入站”數據,編碼處理“出站”數據。知道了編碼和解碼器,對於下面的情況不會感覺驚訝:- byte-to-message編碼和解碼
- message-to-message編碼和解碼
7.4.1 byte-to-byte編解碼器
public class ByteArrayDecoder extends MessageToMessageDecoder<ByteBuf> {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
// copy the ByteBuf content to a byte array
byte[] array = new byte[msg.readableBytes()];
msg.getBytes(0, array);
out.add(array);
}
}
@Sharable
public class ByteArrayEncoder extends MessageToMessageEncoder<byte[]> {
@Override
protected void encode(ChannelHandlerContext ctx, byte[] msg, List<Object> out) throws Exception {
out.add(Unpooled.wrappedBuffer(msg));
}
}
7.4.2 ByteToMessageCodec
ByteToMessageCodec用來處理byte-to-message和message-to-byte。如果想要解碼字節消息成POJO或編碼POJO消息成字節,對於這種情況,ByteToMessageCodec<I>是一個不錯的選擇。ByteToMessageCodec是一種組合,其等同於ByteToMessageDecoder和MessageToByteEncoder的組合。MessageToByteEncoder是個抽象類,其中有2個方法需要我們自己實現:- encode(ChannelHandlerContext, I, ByteBuf),編碼
- decode(ChannelHandlerContext, ByteBuf, List<Object>),解碼
7.4.3 MessageToMessageCodec
- encode(ChannelHandlerContext, OUTBOUND_IN, List<Object>)
- decode(ChannelHandlerContext, INBOUND_IN, List<Object>)
package netty.in.action;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.websocketx.BinaryWebSocketFrame;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.ContinuationWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
@Sharable
public class WebSocketConvertHandler extends
MessageToMessageCodec<WebSocketFrame, WebSocketConvertHandler.MyWebSocketFrame> {
public static final WebSocketConvertHandler INSTANCE = new WebSocketConvertHandler();
@Override
protected void encode(ChannelHandlerContext ctx, MyWebSocketFrame msg, List<Object> out) throws Exception {
switch (msg.getType()) {
case BINARY:
out.add(new BinaryWebSocketFrame(msg.getData()));
break;
case CLOSE:
out.add(new CloseWebSocketFrame(true, 0, msg.getData()));
break;
case PING:
out.add(new PingWebSocketFrame(msg.getData()));
break;
case PONG:
out.add(new PongWebSocketFrame(msg.getData()));
break;
case TEXT:
out.add(new TextWebSocketFrame(msg.getData()));
break;
case CONTINUATION:
out.add(new ContinuationWebSocketFrame(msg.getData()));
break;
default:
throw new IllegalStateException("Unsupported websocket msg " + msg);
}
}
@Override
protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, List<Object> out) throws Exception {
if (msg instanceof BinaryWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.BINARY, msg.content().copy()));
return;
}
if (msg instanceof CloseWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.CLOSE, msg.content().copy()));
return;
}
if (msg instanceof PingWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.PING, msg.content().copy()));
return;
}
if (msg instanceof PongWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.PONG, msg.content().copy()));
return;
}
if (msg instanceof TextWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.TEXT, msg.content().copy()));
return;
}
if (msg instanceof ContinuationWebSocketFrame) {
out.add(new MyWebSocketFrame(MyWebSocketFrame.FrameType.CONTINUATION, msg.content().copy()));
return;
}
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;
}
}
}
7.5 其他編解碼方式
使用編解碼器來充當編碼器和解碼器的組合失去了單獨使用編碼器或解碼器的靈活性,編解碼器是要麼都有要麼都沒有。你可能想知道是否有解決這個僵化問題的方式,還可以讓編碼器和解碼器在ChannelPipeline中作爲一個邏輯單元。幸運的是,Netty提供了一種解決方案,使用CombinedChannelDuplexHandler。雖然這個類不是編解碼器API的一部分,但是它經常被用來簡歷一個編解碼器。7.5.1 CombinedChannelDuplexHandler
/**
* 解碼器,將byte轉成char
* @author c.k
*
*/
public class ByteToCharDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
while(in.readableBytes() >= 2){
out.add(Character.valueOf(in.readChar()));
}
}
}
/**
* 編碼器,將char轉成byte
* @author Administrator
*
*/
public class CharToByteEncoder extends MessageToByteEncoder<Character> {
@Override
protected void encode(ChannelHandlerContext ctx, Character msg, ByteBuf out) throws Exception {
out.writeChar(msg);
}
}
/**
* 繼承CombinedChannelDuplexHandler,用於綁定解碼器和編碼器
* @author c.k
*
*/
public class CharCodec extends CombinedChannelDuplexHandler<ByteToCharDecoder, CharToByteEncoder> {
public CharCodec(){
super(new ByteToCharDecoder(), new CharToByteEncoder());
}
}
從上面代碼可以看出,使用CombinedChannelDuplexHandler綁定解碼器和編碼器很容易實現,比使用*Codec更靈活。- Google的protobuf,在io.netty.handler.codec.protobuf包下
- Google的SPDY協議
- RTSP(Real Time Streaming Protocol,實時流傳輸協議),在io.netty.handler.codec.rtsp包下
- SCTP(Stream Control Transmission Protocol,流控制傳輸協議),在io.netty.handler.codec.sctp包下
- ......