標籤:
我們接下來就看和業務息息相關的解碼器,首先我們來看FrameDecoder,這個東西應該是所有的解碼器都會實現這個,所以我們來重點看一下。
FrameDecoder產生的根源就是TCP/IP資料包的傳輸方式決定的,包在傳輸的過程中會分區和重組,
正如javadoc裡面所說的:
用戶端在發送的時候的序列如下:
+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+
伺服器端在接受到後可能會變成下面的序列:
+----+-------+---+---+
| AB | CDEFG | H | I |
+----+-------+---+---+
FrameDecoder協助我們將接受到的資料包整理成有意義的資料幀,例如,可以協助我們將資料包整理成
下面的資料格式:
+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+
我們接下來就看看FrameDecoder的實現吧:
FrameDecoder是繼承SimpleChannelUpstreamHandler的,我們首先來看一下messageReceived的實現:
Java代碼
- @Override
- public void messageReceived(
- ChannelHandlerContext ctx, MessageEvent e) throws Exception {
-
- Object m = e.getMessage();
- if (!(m instanceof ChannelBuffer)) {
- ctx.sendUpstream(e);
- return;
- }
-
- ChannelBuffer input = (ChannelBuffer) m;
- if (!input.readable()) {
- return;
- }
-
- ChannelBuffer cumulation = cumulation(ctx);
- if (cumulation.readable()) {
- cumulation.discardReadBytes();
- cumulation.writeBytes(input);
- callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress());
- } else {
- callDecode(ctx, e.getChannel(), input, e.getRemoteAddress());
- if (input.readable()) {
- cumulation.writeBytes(input);
- }
- }
- }
這個裡面首先會檢查input的可讀性,這個比較好理解,關鍵是cumulation,
我們首先來看一下cumulation的實現吧:
Java代碼
- private ChannelBuffer cumulation(ChannelHandlerContext ctx) {
- ChannelBuffer c = cumulation;
- if (c == null) {
- c = ChannelBuffers.dynamicBuffer(
- ctx.getChannel().getConfig().getBufferFactory());
- cumulation = c;
- }
- return c;
- }
這個函數很簡單,就是如果cumulation為空白的時候初始化一下,如果不為空白,就返回。我們得思考一下什麼時候cumulation為空白,什麼時候不為空白。我們再回過頭來看一下上面的實現吧。如果cumulation可讀,cumulation.discardReadBytes函數的作用是將0到readIndex之間的空間釋放掉,將readIndex和writeIndex都重新標記一下。然後將讀到的資料寫到buffer裡面。如果cumulation不可讀,在調callDecode,如果發現從不可讀狀態到可讀狀態,則將讀到的資料寫到緩衝區裡面。
我們再來看callDecode的實現:
Java代碼
- private void callDecode(
- ChannelHandlerContext context, Channel channel,
- ChannelBuffer cumulation, SocketAddress remoteAddress) throws Exception {
-
- while (cumulation.readable()) {
- int oldReaderIndex = cumulation.readerIndex();
- Object frame = decode(context, channel, cumulation);
- if (frame == null) {
- if (oldReaderIndex == cumulation.readerIndex()) {
- // Seems like more data is required.
- // Let us wait for the next notification.
- break;
- } else {
- // Previous data has been discarded.
- // Probably it is reading on.
- continue;
- }
- } else if (oldReaderIndex == cumulation.readerIndex()) {
- throw new IllegalStateException(
- "decode() method must read at least one byte " +
- "if it returned a frame (caused by: " + getClass() + ")");
- }
-
- unfoldAndFireMessageReceived(context, remoteAddress, frame);
- }
-
- if (!cumulation.readable()) {
- this.cumulation = null;
- }
- }
-
這個裡面上面是一個迴圈,首先將讀指標備份一下,decode方法是交個子類實現的一個抽象方這個用來實現具體資料分幀的演算法,從這個裡面看到如果子類沒有讀到一幀資料,則返回null所以下面有一個判斷,是一點資料沒有讀呢,還是讀了一點,如果一點都沒有讀,就不需要再檢測了等下一次messageRecieved進行通知,如果發現讀了一點資料,就調用下一次分幀。如果讀了一幀資料就發送一個通知,unfold是針對讀到的迴圈資料要不要開啟的意思。到最後如果發現不是可讀狀態,
cumulation將會被設定成null。
最後來看一下cleanup的實現
Java代碼
- private void cleanup(ChannelHandlerContext ctx, ChannelStateEvent e)
- throws Exception {
- try {
- ChannelBuffer cumulation = this.cumulation;
- if (cumulation == null) {
- return;
- } else {
- this.cumulation = null;
- }
-
- if (cumulation.readable()) {
- // Make sure all frames are read before notifying a closed channel.
- callDecode(ctx, ctx.getChannel(), cumulation, null);
- }
-
- // Call decodeLast() finally. Please note that decodeLast() is
- // called even if there‘s nothing more to read from the buffer to
- // notify a user that the connection was closed explicitly.
- Object partialFrame = decodeLast(ctx, ctx.getChannel(), cumulation);
- if (partialFrame != null) {
- unfoldAndFireMessageReceived(ctx, null, partialFrame);
- }
- } finally {
- ctx.sendUpstream(e);
- }
- }
在這個裡面一般來說是在socket斷開的時候調用,這個時候如果發現buffer還是可讀狀態,還會努力的確保所有的資料已經被分幀,然後調用decodeLast
===========================================================================================
轉自http://blog.csdn.net/xiaolang85/article/details/12621663
netty 學習 FrameDecoder