其他不想说 ,直接上代码,不完善,很简单,基本和netty3的差不多,个人认为是这样,变化的,如果理解netty3,那么看下change note 就会明白。
package commons.netty4.x;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
/**
* @author : 陈磊 <br/>
* Date: 13-3-11<br/>
* Time: 上午11:32<br/>
* connectMethod:13638363871@163.com<br/>
* A special ChannelStateHandler which offers an
* easy way to initialize a Channel once it was registered
* to its EventLoop. Implementations are most often used in
* the context of AbstractBootstrap.handler(ChannelHandler) ,
* AbstractBootstrap.handler(ChannelHandler) and ServerBootstrap.childHandler(ChannelHandler) to
* setup the ChannelPipeline of a Channel.
* Be aware that this class is marked as ChannelHandler.Sharable and
* so the implementation must be safe to be re-used.(复用)
*/
@Sharable
public class ServerChannelInitializer extends ChannelInitializer {
/**
* This method will be called once the {@link io.netty.channel.Channel} was registered. After the method returns this instance
* will be removed from the {@link io.netty.channel.ChannelPipeline} of the {@link io.netty.channel.Channel}.
*
* @param channel the {@link io.netty.channel.Channel} which was registered.
* @throws Exception is thrown if an error accours. In that case the {@link io.netty.channel.Channel} will be closed.
*/
@Override
protected void initChannel(Channel channel) throws Exception {
ChannelPipeline pipeline = channel.pipeline();
// pipeline.addLast()
}
}
package commons.netty4.x;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.TooLongFrameException;
/**
* A decoder that splits the received {@link ByteBuf}s dynamically by the
* value of the length field in the message. It is particularly useful when you
* decode a binary message which has an integer header field that represents the
* length of the message body or the whole message.
* <p>
* {@link LengthFieldBasedFrameDecoder} has many configuration parameters so
* that it can decode any message with a length field, which is often seen in
* proprietary client-server protocols. Here are some example that will give
* you the basic idea on which option does what.
*
* <h3>2 bytes length field at offset 0, do not strip header</h3>
*
* The value of the length field in this example is <tt>12 (0x0C)</tt> which
* represents the length of "HELLO, WORLD". By default, the decoder assumes
* that the length field represents the number of the bytes that follows the
* length field. Therefore, it can be decoded with the simplistic parameter
* combination.
* <pre>
* <b>lengthFieldOffset</b> = <b>0</b>
* <b>lengthFieldLength</b> = <b>2</b>
* lengthAdjustment = 0
* initialBytesToStrip = 0 (= do not strip header)
*
* BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes)
* +--------+----------------+ +--------+----------------+
* | Length | Actual Content |----->| Length | Actual Content |
* | 0x000C | "HELLO, WORLD" | | 0x000C | "HELLO, WORLD" |
* +--------+----------------+ +--------+----------------+
* </pre>
*
* <h3>2 bytes length field at offset 0, strip header</h3>
*
* Because we can get the length of the content by calling
* {@link ByteBuf#readableBytes()}, you might want to strip the length
* field by specifying <tt>initialBytesToStrip</tt>. In this example, we
* specified <tt>2</tt>, that is same with the length of the length field, to
* strip the first two bytes.
* <pre>
* lengthFieldOffset = 0
* lengthFieldLength = 2
* lengthAdjustment = 0
* <b>initialBytesToStrip</b> = <b>2</b> (= the length of the Length field)
*
* BEFORE DECODE (14 bytes) AFTER DECODE (12 bytes)
* +--------+----------------+ +----------------+
* | Length | Actual Content |----->| Actual Content |
* | 0x000C | "HELLO, WORLD" | | "HELLO, WORLD" |
* +--------+----------------+ +----------------+
* </pre>
*
* <h3>2 bytes length field at offset 0, do not strip header, the length field
* represents the length of the whole message</h3>
*
* In most cases, the length field represents the length of the message body
* only, as shown in the previous examples. However, in some protocols, the
* length field represents the length of the whole message, including the
* message header. In such a case, we specify a non-zero
* <tt>lengthAdjustment</tt>. Because the length value in this example message
* is always greater than the body length by <tt>2</tt>, we specify <tt>-2</tt>
* as <tt>lengthAdjustment</tt> for compensation.
* <pre>
* lengthFieldOffset = 0
* lengthFieldLength = 2
* <b>lengthAdjustment</b> = <b>-2</b> (= the length of the Length field)
* initialBytesToStrip = 0
*
* BEFORE DECODE (14 bytes) AFTER DECODE (14 bytes)
* +--------+----------------+ +--------+----------------+
* | Length | Actual Content |----->| Length | Actual Content |
* | 0x000E | "HELLO, WORLD" | | 0x000E | "HELLO, WORLD" |
* +--------+----------------+ +--------+----------------+
* </pre>
*
* <h3>3 bytes length field at the end of 5 bytes header, do not strip header</h3>
*
* The following message is a simple variation of the first example. An extra
* header value is prepended to the message. <tt>lengthAdjustment</tt> is zero
* again because the decoder always takes the length of the prepended data into
* account during frame length calculation.
* <pre>
* <b>lengthFieldOffset</b> = <b>2</b> (= the length of Header 1)
* <b>lengthFieldLength</b> = <b>3</b>
* lengthAdjustment = 0
* initialBytesToStrip = 0
*
* BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes)
* +----------+----------+----------------+ +----------+----------+----------------+
* | Header 1 | Length | Actual Content |----->| Header 1 | Length | Actual Content |
* | 0xCAFE | 0x00000C | "HELLO, WORLD" | | 0xCAFE | 0x00000C | "HELLO, WORLD" |
* +----------+----------+----------------+ +----------+----------+----------------+
* </pre>
*
* <h3>3 bytes length field at the beginning of 5 bytes header, do not strip header</h3>
*
* This is an advanced example that shows the case where there is an extra
* header between the length field and the message body. You have to specify a
* positive <tt>lengthAdjustment</tt> so that the decoder counts the extra
* header into the frame length calculation.
* <pre>
* lengthFieldOffset = 0
* lengthFieldLength = 3
* <b>lengthAdjustment</b> = <b>2</b> (= the length of Header 1)
* initialBytesToStrip = 0
*
* BEFORE DECODE (17 bytes) AFTER DECODE (17 bytes)
* +----------+----------+----------------+ +----------+----------+----------------+
* | Length | Header 1 | Actual Content |----->| Length | Header 1 | Actual Content |
* | 0x00000C | 0xCAFE | "HELLO, WORLD" | | 0x00000C | 0xCAFE | "HELLO, WORLD" |
* +----------+----------+----------------+ +----------+----------+----------------+
* </pre>
*
* <h3>2 bytes length field at offset 1 in the middle of 4 bytes header,
* strip the first header field and the length field</h3>
*
* This is a combination of all the examples above. There are the prepended
* header before the length field and the extra header after the length field.
* The prepended header affects the <tt>lengthFieldOffset</tt> and the extra
* header affects the <tt>lengthAdjustment</tt>. We also specified a non-zero
* <tt>initialBytesToStrip</tt> to strip the length field and the prepended
* header from the frame. If you don't want to strip the prepended header, you
* could specify <tt>0</tt> for <tt>initialBytesToSkip</tt>.
* <pre>
* lengthFieldOffset = 1 (= the length of HDR1)
* lengthFieldLength = 2
* <b>lengthAdjustment</b> = <b>1</b> (= the length of HDR2)
* <b>initialBytesToStrip</b> = <b>3</b> (= the length of HDR1 + LEN)
*
* BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes)
* +------+--------+------+----------------+ +------+----------------+
* | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
* | 0xCA | 0x000C | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" |
* +------+--------+------+----------------+ +------+----------------+
* </pre>
*
* <h3>2 bytes length field at offset 1 in the middle of 4 bytes header,
* strip the first header field and the length field, the length field
* represents the length of the whole message</h3>
*
* Let's give another twist to the previous example. The only difference from
* the previous example is that the length field represents the length of the
* whole message instead of the message body, just like the third example.
* We have to count the length of HDR1 and Length into <tt>lengthAdjustment</tt>.
* Please note that we don't need to take the length of HDR2 into account
* because the length field already includes the whole header length.
* <pre>
* lengthFieldOffset = 1
* lengthFieldLength = 2
* <b>lengthAdjustment</b> = <b>-3</b> (= the length of HDR1 + LEN, negative)
* <b>initialBytesToStrip</b> = <b> 3</b>
*
* BEFORE DECODE (16 bytes) AFTER DECODE (13 bytes)
* +------+--------+------+----------------+ +------+----------------+
* | HDR1 | Length | HDR2 | Actual Content |----->| HDR2 | Actual Content |
* | 0xCA | 0x0010 | 0xFE | "HELLO, WORLD" | | 0xFE | "HELLO, WORLD" |
* +------+--------+------+----------------+ +------+----------------+
* </pre>
* @see io.netty.handler.codec.LengthFieldPrepender
*/
/**
* @author : 陈磊 <br/>
* Date: 13-3-11<br/>
* Time: 上午10:30<br/>
* connectMethod:13638363871@163.com<br/>
*/
public final class ServerDecoder extends ByteToMessageDecoder {
private final int maxFrameLength;
private final int lengthFieldOffset;
private final int lengthFieldLength;
private final int lengthFieldEndOffset;
private final int lengthAdjustment;
private final int initialBytesToStrip;
private final boolean failFast;
private boolean discardingTooLongFrame;
private long tooLongFrameLength;
private long bytesToDiscard;
/**
* Creates a new instance.
*
* @param maxFrameLength
* the maximum length of the frame. If the length of the frame is
* greater than this value, {@link io.netty.handler.codec.TooLongFrameException} will be
* thrown.
* @param lengthFieldOffset
* the offset of the length field
* @param lengthFieldLength
* the length of the length field
*/
public ServerDecoder(
int maxFrameLength,
int lengthFieldOffset, int lengthFieldLength) {
this(maxFrameLength, lengthFieldOffset, lengthFieldLength, 0, 0);
}
/**
* Creates a new instance.
*
* @param maxFrameLength
* the maximum length of the frame. If the length of the frame is
* greater than this value, {@link io.netty.handler.codec.TooLongFrameException} will be
* thrown.
* @param lengthFieldOffset
* the offset of the length field
* @param lengthFieldLength
* the length of the length field
* @param lengthAdjustment
* the compensation value to add to the value of the length field
* @param initialBytesToStrip
* the number of first bytes to strip out from the decoded frame
*/
public ServerDecoder(
int maxFrameLength,
int lengthFieldOffset, int lengthFieldLength,
int lengthAdjustment, int initialBytesToStrip) {
this(
maxFrameLength,
lengthFieldOffset, lengthFieldLength, lengthAdjustment,
initialBytesToStrip, true);
}
/**
* Creates a new instance.
*
* @param maxFrameLength
* the maximum length of the frame. If the length of the frame is
* greater than this value, {@link io.netty.handler.codec.TooLongFrameException} will be
* thrown.
* @param lengthFieldOffset
* the offset of the length field
* @param lengthFieldLength
* the length of the length field
* @param lengthAdjustment
* the compensation value to add to the value of the length field
* @param initialBytesToStrip
* the number of first bytes to strip out from the decoded frame
* @param failFast
* If <tt>true</tt>, a {@link io.netty.handler.codec.TooLongFrameException} is thrown as
* soon as the decoder notices the length of the frame will exceed
* <tt>maxFrameLength</tt> regardless of whether the entire frame
* has been read. If <tt>false</tt>, a {@link io.netty.handler.codec.TooLongFrameException}
* is thrown after the entire frame that exceeds <tt>maxFrameLength</tt>
* has been read.
*/
public ServerDecoder(
int maxFrameLength,
int lengthFieldOffset, int lengthFieldLength,
int lengthAdjustment, int initialBytesToStrip, boolean failFast) {
if (maxFrameLength <= 0) {
throw new IllegalArgumentException(
"maxFrameLength must be a positive integer: " +
maxFrameLength);
}
if (lengthFieldOffset < 0) {
throw new IllegalArgumentException(
"lengthFieldOffset must be a non-negative integer: " +
lengthFieldOffset);
}
if (initialBytesToStrip < 0) {
throw new IllegalArgumentException(
"initialBytesToStrip must be a non-negative integer: " +
initialBytesToStrip);
}
if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
lengthFieldLength != 3 && lengthFieldLength != 4 &&
lengthFieldLength != 8) {
throw new IllegalArgumentException(
"lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
lengthFieldLength);
}
if (lengthFieldOffset > maxFrameLength - lengthFieldLength) {
throw new IllegalArgumentException(
"maxFrameLength (" + maxFrameLength + ") " +
"must be equal to or greater than " +
"lengthFieldOffset (" + lengthFieldOffset + ") + " +
"lengthFieldLength (" + lengthFieldLength + ").");
}
this.maxFrameLength = maxFrameLength;
this.lengthFieldOffset = lengthFieldOffset;
this.lengthFieldLength = lengthFieldLength;
this.lengthAdjustment = lengthAdjustment;
lengthFieldEndOffset = lengthFieldOffset + lengthFieldLength;
this.initialBytesToStrip = initialBytesToStrip;
this.failFast = failFast;
}
@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
if (discardingTooLongFrame) {
long bytesToDiscard = this.bytesToDiscard;
int localBytesToDiscard = (int) Math.min(bytesToDiscard, in.readableBytes());
in.skipBytes(localBytesToDiscard);
bytesToDiscard -= localBytesToDiscard;
this.bytesToDiscard = bytesToDiscard;
failIfNecessary(ctx, false);
return null;
}
if (in.readableBytes() < lengthFieldEndOffset) {
return null;
}
int actualLengthFieldOffset = in.readerIndex() + lengthFieldOffset;
/** the pack length is 2 byte ,you can change it!*/
long frameLength = in.getUnsignedShort(actualLengthFieldOffset);
/**
switch (lengthFieldLength) {
case 1:
frameLength = in.getUnsignedByte(actualLengthFieldOffset);
break;
case 2:
frameLength = in.getUnsignedShort(actualLengthFieldOffset);
break;
case 3:
frameLength = in.getUnsignedMedium(actualLengthFieldOffset);
break;
case 4:
frameLength = in.getUnsignedInt(actualLengthFieldOffset);
break;
case 8:
frameLength = in.getLong(actualLengthFieldOffset);
break;
default:
throw new Error("should not reach here");
}
*/
if (frameLength < 0) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"negative pre-adjustment length field: " + frameLength);
}
frameLength += lengthAdjustment + lengthFieldEndOffset;
if (frameLength < lengthFieldEndOffset) {
in.skipBytes(lengthFieldEndOffset);
throw new CorruptedFrameException(
"Adjusted frame length (" + frameLength + ") is less " +
"than lengthFieldEndOffset: " + lengthFieldEndOffset);
}
if (frameLength > maxFrameLength) {
// Enter the discard mode and discard everything received so far.
discardingTooLongFrame = true;
tooLongFrameLength = frameLength;
bytesToDiscard = frameLength - in.readableBytes();
in.skipBytes(in.readableBytes());
failIfNecessary(ctx, true);
return null;
}
// never overflows because it's less than maxFrameLength
int frameLengthInt = (int) frameLength;
if (in.readableBytes() < frameLengthInt) {
return null;
}
if (initialBytesToStrip > frameLengthInt) {
in.skipBytes(frameLengthInt);
throw new CorruptedFrameException(
"Adjusted frame length (" + frameLength + ") is less " +
"than initialBytesToStrip: " + initialBytesToStrip);
}
in.skipBytes(initialBytesToStrip);
// extract frame
int readerIndex = in.readerIndex();
int actualFrameLength = frameLengthInt - initialBytesToStrip;
ByteBuf frame = extractFrame(in, readerIndex, actualFrameLength);
in.readerIndex(readerIndex + actualFrameLength);
return frame;
}
private void failIfNecessary(ChannelHandlerContext ctx, boolean firstDetectionOfTooLongFrame) {
if (bytesToDiscard == 0) {
// Reset to the initial state and tell the handlers that
// the frame was too large.
long tooLongFrameLength = this.tooLongFrameLength;
this.tooLongFrameLength = 0;
discardingTooLongFrame = false;
if (!failFast ||
failFast && firstDetectionOfTooLongFrame) {
fail(ctx, tooLongFrameLength);
}
} else {
// Keep discarding and notify handlers if necessary.
if (failFast && firstDetectionOfTooLongFrame) {
fail(ctx, tooLongFrameLength);
}
}
}
/**
* Extract the sub-region of the specified buffer.
* <p>
* If you are sure that the frame and its content are not accessed after
* the current {@link #decode(ChannelHandlerContext, ByteBuf)}
* call returns, you can even avoid memory copy by returning the sliced
* sub-region (i.e. <tt>return buffer.slice(index, length)</tt>).
* It's often useful when you convert the extracted frame into an object.
* Refer to the source code of {@link io.netty.handler.codec.serialization.ObjectDecoder} to see how this method
* is overridden to avoid memory copy.
*/
protected ByteBuf extractFrame(ByteBuf buffer, int index, int length) {
ByteBuf frame = Unpooled.buffer(length);
frame.writeBytes(buffer, index, length);
return frame;
}
private void fail(ChannelHandlerContext ctx, long frameLength) {
if (frameLength > 0) {
ctx.fireExceptionCaught(
new TooLongFrameException(
"Adjusted frame length exceeds " + maxFrameLength +
": " + frameLength + " - discarded"));
} else {
ctx.fireExceptionCaught(
new TooLongFrameException(
"Adjusted frame length exceeds " + maxFrameLength +
" - discarding"));
}
}
}
package commons.netty4.x;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
/**
* @author : 陈磊 <br/>
* Date: 13-3-11<br/>
* Time: 上午10:36<br/>
* connectMethod:13638363871@163.com<br/>
* Indicates that the same instance of the annotated {@link io.netty.channel.ChannelHandler}
* can be added to one or more {@link io.netty.channel.ChannelPipeline}s multiple times
* without a race condition.
* As long as you have the reference to the Channel (or ChannelHandlerContext),
* you can call Channel.write()
* (or ChannelHandlerContext.write()) from anywhere, any thread.See discussion.
*/
@Sharable
public class ServerEncoder extends MessageToMessageEncoder {
/**
*
* Encode from one message to an other. This method will be called till either the {@link io.netty.buffer.MessageBuf} has nothing
* left or till this method returns {@code null}.
*
* @param ctx the {@link io.netty.channel.ChannelHandlerContext} which this {@link io.netty.handler.codec.MessageToMessageEncoder} belongs to
* @param msg the message to encode to an other one
* @return message the encoded message or {@code null} if more messages are needed be cause the implementation
* needs to do some kind of aggragation
* @throws Exception is thrown if an error accour
*/
@Override
protected Object encode(ChannelHandlerContext ctx, Object msg) throws Exception {
// ctx.channel().write()
return null; //To change body of implemented methods use File | Settings | File Templates.
}
}
package commons.netty4.x;
import commons.log.Logger;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
/**
* @author : 陈磊 <br/>
* Date: 13-3-11<br/>
* Time: 下午12:20<br/>
* connectMethod:13638363871@163.com<br/>
*/
public class ServerHandler extends ChannelInboundMessageHandlerAdapter {
private static final Logger logger = Logger.getLogger(
ServerHandler.class.getName());
/**
* Is called once a message was received.
*
* @param ctx the {@link io.netty.channel.ChannelHandlerContext} which this {@link io.netty.channel.ChannelHandler} belongs to
* @param msg the message to handle
* ByteToMessageDecoder--->callDecode(ctx, in)===(ServerFieldBasedFrameDecoder decode())---> inboundBufferUpdated()将会把 buf中的数据poll----> messageReceived();
*/
@Override
protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
///Your blocking application code
}
/**
* Calls {@link ChannelHandlerContext#fireChannelActive()} to forward
* to the next {@link io.netty.channel.ChannelStateHandler} in the {@link io.netty.channel.ChannelPipeline}.
*
* Sub-classes may override this method to change behavior.
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive();
}
/**
* Calls {@link ChannelHandlerContext#fireChannelInactive()} to forward
* to the next {@link io.netty.channel.ChannelStateHandler} in the {@link io.netty.channel.ChannelPipeline}.
*
* Sub-classes may override this method to change behavior.
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelInactive();
}
// public void exceptionCaught(ChannelHandlerContext ctx,
// Throwable cause) throws Exception{
//// logger
// ctx.close();
// }
/**
* clear Pipeline
* @param ctx
*/
private void clearPipeline(ChannelHandlerContext ctx){
// ctx.channel().pipeline().removeLast();
}
}
package commons.netty4.x;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.CombinedChannelDuplexHandler;
import io.netty.handler.timeout.IdleState;
/**
* @author : 陈磊 <br/>
* Date: 13-3-11<br/>
* Time: 下午3:03<br/>
* connectMethod:13638363871@163.com<br/>
* 链接超时处理
* Handler should handle the IdleStateEvent triggered by IdleStateHandler.
*/
public class ServerIdleStateHandler extends CombinedChannelDuplexHandler {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception{
if (evt instanceof IdleState){
IdleState e= (IdleState) evt;
if (e== IdleState.READER_IDLE){
ctx.channel().close();
}else if (e==IdleState.WRITER_IDLE){
ctx.channel().close();
}
}
}
}
package commons.netty4.x;
import commons.BaseConfig.config;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.log4j.xml.DOMConfigurator;
/**
* @author : 陈磊 <br/>
* Date: 13-3-11<br/>
* Time: 下午4:08<br/>
* connectMethod:13638363871@163.com<br/>
*/
public class ServerStart implements IServer{
private final int port;
public ServerStart(int port){
this.port = port;
}
/**
* run netty server
*/
@Override
public void run() throws Exception {
ServerBootstrap serverBootstrap = new ServerBootstrap();
try {
serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup())
.channel(NioServerSocketChannel.class).childHandler(new ServerChannelInitializer());
/** Bind and start to accept incoming connections.*/
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
/**
Wait until the server socket is closed.
In this example, this does not happen, but you can do that to gracefully
shut down your server.
*/
channelFuture.channel().closeFuture().sync();
}finally {
serverBootstrap.shutdown();
}
}
public static void main(String[] args) throws Exception {
//日志初始化
DOMConfigurator.configure(config.Game_log4j);
new ServerStart(7777).run();
}
}
package commons.netty4.x;
/**
* @author : 陈磊 <br/>
* Date: 13-3-11<br/>
* Time: 下午4:06<br/>
* connectMethod:13638363871@163.com<br/>
*/
public interface IServer {
/**
* run netty server
*/
void run()throws Exception;
}
只是大概的搭建了一个,如果用于生产环境,请细看对于操作用时较多的模块怎么处理!文档中有提供解决方案
http://netty.io/4.0/guide/#faq.3,基本也就这样。netty的buf是有很大的改进,动态处理,在write之后,自动回收哦,具体请看api