FrameDecoderは通信プロトコルの実装に活用するといいらしい。
ハンドラー側で通信プロトコルも実装することはできるが、
ハンドラーはビジネスロジックの処理に集中させるべきで、
パイプラインで通信プロトコル部分の並列処理ができるというメリットもあるので、
通信プロトコルはFrameDecoderで実装するのがベター。
で、FrameDecoderの基本的な使い方は、
1. 実装する通信プロトコルのデコードに必要なデータが受信バッファに溜まるまで待つ
2. デコード処理
3. デコード結果を任意のオブジェクトで戻すことでハンドラに渡す
という感じ。
この中で一番重要なのが、1のバッファが満たされるまで待つという部分。
ノンブロッキングで受信すると、受信バッファにデータが溜まっていくが、
TCP/IPはネットワークの輻輳を避けるため、まとめて送ることができるデータは
できるだけまとめようとする。
この機能により、高速に通信を繰り返すようなケースで、パケットがすぐに送信されずに、
ある程度まとまってから送信される場合がある。
逆に、合計で100バイトを送信するのに、1バイト単位で100回データをwriteするといったことを行うと、
パケット分割が発生するケースもある。
これらが発生して何が困るかというと、
受信イベント(最終的にハンドラーのmessageReceivedが呼び出される)を受け取った際に、
受信バッファに必ずしも、通信プロトコルで期待したデータ量ぴったりのデータが届いているわけではなく、
少なかったり(パケット分割が発生したケース)、多かったり(まとめて送られたケース)といった現象が発生する。
その辺の挙動を吸収する処理をFrameDecoderにまとめた方がよい。
ってことらしい(確信持てず)。
なお、FrameDecoderは受信側で、パケットの分割、まとめて受信を吸収するという考え方だが、
送信側で、通信プロトコル単位できっちり送信する方法がNettyにあるかもしれない。
最終的にTCP/IPで送るなら、MSS以下の単位で送信を繰り返すとかになりそうだけど。
受信した内容をそのままスルーする(ようするにハンドラ単体と同じ挙動)を実現するには
下記のようなコードになる。
public class SampleFrameDecoder extends FrameDecoder { @Override protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { // そのまま流す ChannelBuffer buf = ChannelBuffers.buffer(buffer.readableBytes()); buffer.readBytes(buf); return buf; } }