参考文献:タイムオタクフー建の先生「ネッティーソースコード解析と実用的な」の話は私のコードをcheap.showです!
スティックパックと半分パックとは何ですか
クライアントがデータを送信すると、データが実際に内部のTCP送信バッファに書き込まれます。
ハーフパック:名は、パケットサイズは、パケットが複数のソケットを介してサーバに送信された複数のパケットに分割されているTCPより送信バッファ容量から送信された場合の半分、パケットを受信すると示唆している、最初のサーバー取得内部のデータキャッシュを受け入れるための時間から、実際には、全体のパッケージの一部であり、半分パックは唯一のオールインクルーシブの半分を受け、全体のパッケージの一部を受けると言われていると言うことではありません。
バッファを受け入れるように、サーバからのTCP送信バッファサイズを介して送信されるパケットのサイズが小さく、キャッシュは複数のTCPパケットを格納することができた場合は、通信クライアントとサーバが複数のパケットを送信することが可能で、この時がありますスティックパッケージパッケージの詳細を読みます
栗の場合:
私たちは今、2つのメッセージを送信している:「ABC」は、「DEF」は 、 その後、他の受信されたメッセージがこのフォーマットである必要はありませんが、彼らはただ一度だけ「ABCDEF」を受けたことがあり、また、いくつかの回は「AB」を受信分割することができます、「CD」、「EF」 、 またはメッセージが2の「a」、「BCDEF」を受信するとさらに悪化。そして、このようなメッセージが二つの現象スティックパッケージと呼ばれている受信、受信3回4つの現象は複数のパック現象不完全半分です。
スティックパッケージの主な理由:
データが送信者<ソケットバッファサイズに書き込まれるたびに、受信機はデータソケットバッファが十分な時間ではありません読み込みます。
主な理由の半分パック:
送信側は、データ伝送プロトコル(最大伝送単位MTU)必要アンパックより大きい場合がある、データのMTU>ソケットバッファサイズを書き込みます。(MTUは、実際には、各TCPプロトコルのサイズです)
別の角度:
トランシーバ:送信が複数回受信することができ、複数の送信を一旦受信することができます
送信:送信は、共通の送信パケット透過複数の送信パケットの複数を占めることができます
根本的原因:
TCPプロトコルフロー、メッセージなしの境界です。(UDPは、小包郵便として、複数の輸送が、しかし、各パッケージには、「境界」、記号、ありませんのでスティックパッケージ、半分パックの問題があります)
この問題を解決するための基本的な手段:境界のメッセージを見つけるために
Netty对三种常用封帧方式的支持(三个类都继承ByteTomessageDecoder.java)
解读Netty处理粘包、半包的源码-------解码核心工作流程:
先找到ByteToMessageDecoder.java,可以看到它继承了ChannelInboundHandlerAdapter.java
这个ChannelInboundHandlerAdapter有个核心的入口方法“channelRead()”;
图中的msg就相当于我们的数据,开始就把数据转换成data,
ByteBuf data = (ByteBuf) msg;
然后判断cumulation是否为null,cumulation是数据积累器,用来积累数据。
ByteBuf cumulation;
first = cumulation == null; if (first) { cumulation = data; } else { cumulation = cumulator.cumulate(ctx.alloc(), cumulation, data); }
第一次的时候肯定为null 所以first是true,直接把data数据给了cumulation。再接下来就是接码:
callDecode(ctx, cumulation, out);
进入具体方法
参数中的in就是数据积累器中的数据,也就是我们传入的数据。往下走又这么个方法调用:
decodeRemovalReentryProtection(ctx, in, out);
需要注意的是:decode中时,不能执行完handler remove清理操作,decode完之后需要清理数据,改方法的名称长久是标识功能的。点进去可以查看
可以看出有个decodeState = STATE_CALLING_CHILD_DECODE;这个就是处理刚才remove handler 的;接下来就是“decode(ctx, in, out);”;这个decode是个抽象方法
之前说过Netty对三种封帧的支持分别是:“FixedLengthFrameDecoder”,“DelimiterBasedFrameDecoder”,“LengthFieldBasedFrameDecoder”。这里以“FixedLengthFrameDecoder”为例,所以点开FixedLengthFrameDecoder的decode();
@Override protected final void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded); } }
然后再进去查看Object decoded = decode(ctx, in);
protected Object decode( @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, ByteBuf in) throws Exception { if (in.readableBytes() < frameLength) { return null; } else { return in.readRetainedSlice(frameLength); } }
这时候就能发现关键点,代码标粗,这个in还是数据积累器的数据,取得之后进行判断,如果小于则返回null不能解出,否则解出。这样一来就完成了一个数据解析的过程。