References: Time Geeks Fu Jian teacher "Netty source code analysis and practical" Talk is cheap.show me the code!
What is the stick pack and half a pack
When the client sends data, data is actually written to the TCP transmission buffer inside.
Half pack: name suggests is half received packet, if the packet size transmitted from the transmission buffer capacity than TCP, then the packet is divided into a plurality of packets, sent to the server through multiple socket, a first server from time to accept the data cache inside the acquisition, is actually part of the whole package, half a pack not to say that only received half of the all-inclusive, and is said to receive a part of the whole package.
Stick package: If the size of packets sent over TCP send buffer size is small, and the cache can store multiple TCP packet, then a communications client and server may transmit multiple packets, this time from the server to accept the buffer may read more about the package
For chestnut:
We are now sending two messages: "ABC", "DEF" , then the other received message is not necessarily in this format, they may just received a one-time "ABCDEF", may also be divided several times received the "AB" , "CD", "EF" , or even worse once the message is received two "a", "BCDEF". Then receiving such a message is called a stick package two phenomena , a plurality of the received three times four phenomenon is incomplete half a pack phenomenon.
Stick package main reasons:
Each time data is written to the sender <socket buffer size, the receiver reads the data socket buffer is not enough time.
Half a pack of the main reasons:
The sender writes data MTU> socket buffer size, there is the data transmission is greater than a protocol (Maximum Transmission Unit MTU) when necessary unpacking. (MTU is actually the size of each TCP protocol)
Another angle:
A transceiver: a transmission can be received multiple times, multiple transmissions may be received once
Transmission: transmission may occupy a plurality of transmission packets, a plurality of common transmission packet transmittable
root cause:
TCP is a protocol flow, no message boundaries. (UDP as parcel post, although more than one transport, but each package has a "boundary", a a sign, so no stick package, half a pack issue)
Fundamental means to solve the problem: to find the boundary message
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不能解出,否则解出。这样一来就完成了一个数据解析的过程。