《Netty In Action中文版》第七章:轉碼器Codec,nettyinaction
本章介紹
- Codec,轉碼器
- Decoder,解碼器
- Encoder,編碼器
Netty提供了轉碼器架構,使得編寫自訂的轉碼器很容易,並且也很容易重用和封裝。本章討論Netty的轉碼器架構以及使用。
7.1 轉碼器Codec
編寫一個網路應用程式需要實現某種轉碼器,轉碼器的作用就是講原始位元組資料與自訂的訊息對象進行互轉。網路中都是以位元組碼的資料形式來傳輸資料的,伺服器編碼資料後發送到用戶端,用戶端需要對資料進行解碼,因為轉碼器由兩部分組成:
- Decoder(解碼器)
- Encoder(編碼器)
解碼器負責將訊息從位元組或其他序列形式轉成指定的訊息對象,編碼器則相反;解碼器負責處理“入站”資料,編碼器負責處理“出站”資料。編碼器和解碼器的結構很簡單,訊息被編碼後解碼後會自動通過ReferenceCountUtil.release(message)釋放,如果不想釋放訊息可以使用ReferenceCountUtil.retain(message),這將會使引用數量增加而沒有訊息發布,大多數時候不需要這麼做。7.2 解碼器
Netty提供了豐富的解碼器抽象基類,我們可以很容易的實現這些基類來自訂解碼器。下面是解碼器的一個類型:
- 解碼位元組到訊息
- 解碼訊息到訊息
- 解碼訊息到位元組
本章將概述不同的抽象基類,來協助瞭解解碼器的實現。深入瞭解Netty提供的解碼器之前先瞭解解碼器的作用是什嗎?解碼器負責解碼“入站”資料從一種格式到另一種格式,解碼器處理入站資料是抽象ChannelInboundHandler的實現。實踐中使用解碼器很簡單,就是將入站資料轉換格式後傳遞到ChannelPipeline中的下一個ChannelInboundHandler進行處理;這樣的處理時很靈活的,我們可以將解碼器放在ChannelPipeline中,重用邏輯。7.2.1 ByteToMessageDecoder
通常你需要將訊息從位元組解碼成訊息或者從位元組解碼成其他的序列化位元組。這是一個常見的任務,Netty提供了抽象基類,我們可以使用它們來實現。Netty中提供的ByteToMessageDecoder可以將位元組訊息解碼成POJO對象,下面列出了ByteToMessageDecoder兩個主要方法:
- decode(ChannelHandlerContext, ByteBuf, List<Object>),這個方法是唯一的一個需要自己實現的抽象方法,作用是將ByteBuf資料解碼成其他形式的資料。
- decodeLast(ChannelHandlerContext, ByteBuf, List<Object>),實際上調用的是decode(...)。
例如伺服器從某個用戶端接收到一個整數值的位元組碼,伺服器將資料讀入ByteBuf並經過ChannelPipeline中的每個ChannelInboundHandler進行處理,看:
顯示了從“入站”ByteBuf讀取bytes後由ToIntegerDecoder進行解碼,然後向解碼後的訊息傳遞到ChannelPipeline中的下一個ChannelInboundHandler。看下面ToIntegerDecoder的實現代碼:
/** * Integer解碼器,ByteToMessageDecoder實現 * @author c.k * */public class ToIntegerDecoder extends ByteToMessageDecoder {@Overrideprotected 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()大部分時間不會返回期望值
如果你能忍受上面列出的限制,相比ByteToMessageDecoder,你可能更喜歡ReplayingDecoder。在滿足需求的情況下推薦使用ByteToMessageDecoder,因為它的處理比較簡單,沒有ReplayingDecoder實現的那麼複雜。ReplayingDecoder繼承與ByteToMessageDecoder,所以他們提供的介面是相同的。下面代碼是ReplayingDecoder的實現:
/** * Integer解碼器,ReplayingDecoder實現 * @author c.k * */public class ToIntegerReplayingDecoder extends ReplayingDecoder<Void> {@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {out.add(in.readInt());}} 當從接收的資料ByteBuf讀取integer,若沒有足夠的位元組可讀,decode(...)會停止解碼,若有足夠的位元組可讀,則會讀取資料添加到List列表中。使用ReplayingDecoder或ByteToMessageDecoder是個人喜好的問題,Netty提供了這兩種實現,選擇哪一個都可以。 上面講了byte-to-message的解碼實現方式,那message-to-message該如何?呢?Netty提供了MessageToMessageDecoder抽象類別。
7.2.3 MessageToMessageDecoder
將訊息對象轉成訊息對象可是使用MessageToMessageDecoder,它是一個抽象類別,需要我們自己實現其decode(...)。message-to-message同上面講的byte-to-message的處理機制一樣,看:
看下面的實現代碼:
/** * 將接收的Integer訊息轉成String類型,MessageToMessageDecoder實現 * @author c.k * */public class IntegerToStringDecoder extends MessageToMessageDecoder<Integer> {@Overrideprotected void decode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {out.add(String.valueOf(msg));}}7.2.4 解碼器總結
解碼器是用來處理入站資料,Netty提供了很多解碼器的實現,可以根據需求詳細瞭解。那我們發送資料需要將資料編碼,Netty中也提供了編碼器的支援。下一節將講解如何?編碼器。7.3 編碼器
Netty提供了一些基類,我們可以很簡單的編碼器。同樣的,編碼器有下面兩種類型:
相對解碼器,編碼器少了一個byte-to-byte的類型,因為出站資料這樣做沒有意義。編碼器的作用就是將處理好的資料轉成位元組碼以便在網路中傳輸。對照上面列出的兩種編碼器類型,Netty也分別提供了兩個抽象類別:MessageToByteEncoder和MessageToMessageEncoder。下面是類別關係圖:
7.3.1 MessageToByteEncoder
MessageToByteEncoder是抽象類別,我們自訂一個繼承MessageToByteEncoder的編碼器只需要實現其提供的encode(...)方法。其工作流程如:
實現代碼如下:
/** * 編碼器,將Integer值編碼成byte[],MessageToByteEncoder實現 * @author c.k * */public class IntegerToByteEncoder extends MessageToByteEncoder<Integer> {@Overrideprotected 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> {@Overrideprotected void encode(ChannelHandlerContext ctx, Integer msg, List<Object> out) throws Exception {out.add(String.valueOf(msg));}}7.4 轉碼器
實際編碼中,一般會將編碼和解碼操作封裝太一個類中,解碼處理“入站”資料,編碼處理“出站”資料。知道了編碼和解碼器,對於下面的情況不會感覺驚訝:
- byte-to-message編碼和解碼
- message-to-message編碼和解碼
如果確定需要在ChannelPipeline中使用編碼器和解碼器,需要更好的使用一個抽象的轉碼器。同樣,使用轉碼器的時候,不可能只刪除解碼器或編碼器而離開ChannelPipeline導致某種不一致的狀態。使用轉碼器將強制性的要麼都在ChannelPipeline,要麼都不在ChannelPipeline。 考慮到這一點,我們在下面幾節將更深入的分析Netty提供的編解碼抽象類別。7.4.1 byte-to-byte轉碼器
Netty4較之前的版本,其結構有很大的變化,在Netty4中實現byte-to-byte提供了2個類:ByteArrayEncoder和ByteArrayDecoder。這兩個類用來處理位元組到位元組的編碼和解碼。下面是這兩個類的源碼,一看就知道是如何處理的:
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); }}
@Sharablepublic 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
MessageToMessageCodec用於message-to-message的編碼和解碼,可以看成是MessageToMessageDecoder和MessageToMessageEncoder的組合體。MessageToMessageCodec是抽象類別,其中有2個方法需要我們自己實現:
- encode(ChannelHandlerContext, OUTBOUND_IN, List<Object>)
- decode(ChannelHandlerContext, INBOUND_IN, List<Object>)
但是,這種轉碼器能有用嗎?
有許多用例,最常見的就是需要將訊息從一個API轉到另一個API。這種情況下需要自訂API或舊的API使用另一種訊息類型。下面的代碼顯示了在WebSocket架構APIs之間轉換訊息:
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;@Sharablepublic class WebSocketConvertHandler extendsMessageToMessageCodec<WebSocketFrame, WebSocketConvertHandler.MyWebSocketFrame> {public static final WebSocketConvertHandler INSTANCE = new WebSocketConvertHandler();@Overrideprotected 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);}}@Overrideprotected 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
如何使用CombinedChannelDuplexHandler來結合解碼器和編碼器呢?下面我們從兩個簡單的例子看瞭解。
/** * 解碼器,將byte轉成char * @author c.k * */public class ByteToCharDecoder extends ByteToMessageDecoder {@Overrideprotected 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> {@Overrideprotected 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更靈活。Netty還提供了其他的協議支援,放在io.netty.handler.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包下
- ......
在什地方可以下載FLV1 codec(轉碼器)?
FLV?
個人感覺應該是你的AIV缺少視頻解碼。
下載FDDSHOW或者完整安裝暴風影音(MPC)應該可以解決問題。
試一下FDDSHOW吧!
ffdshow MPEG-4 Video Decoder 20080811 Rev2073
www.onlinedown.net/soft/5636.htm
GOOD LUCK!
1音效卡主要由數字控制器與音頻訊號轉碼器兩部分組成 2CODEC晶片用於數字聲音訊號處理 這兩句話那句正確
音效卡主要由 數位訊號處理器 多媒體數字訊號轉碼器組成
.CODEC晶片用於對原始聲音訊號的採樣混音處理