其他不想说 ,直接上代码,不完善,很简单,基本和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:[email protected]<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:[email protected]<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:[email protected]<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:[email protected]<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:[email protected]<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:[email protected]<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:[email protected]<br/> */ public interface IServer { /** * run netty server */ void run()throws Exception; }只是大概的搭建了一个,如果用于生产环境,请细看对于操作用时较多的模块怎么处理!文档中有提供解决方案 http://netty.io/4.0/guide/#faq.3,基本也就这样。netty的buf是有很大的改进,动态处理,在write之后,自动回收哦,具体请看api