Java tutorial
/* * Copyright (c) 2014. Lorem ipsum dolor sit amet, consectetur adipiscing elit. * http://www.apache.org/licenses/LICENSE-2.0 */ package net.NettyEngine4; import com.dc.gameserver.ServerCore.Controller.AbstractController.IController; import com.dc.gameserver.ServerCore.Service.character.PlayerInstance; import com.google.protobuf.ExtensionRegistryLite; import com.google.protobuf.MessageLite; import com.google.protobuf.UninitializedMessageException; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.BeanFactory; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicInteger; /** * @author :<br/> * Project:CreazyGameServer1.8 * Date: 13-4-12 * Time: ?9:19 * * ChannelStateHandlerAdapter * ChannelInboundByteHandlerAdapter * ChannelDuplexHandler * ChannelInboundHandlerAdapter * * This implementation just forward the operation to the next {@link io.netty.channel.ChannelHandler} in the * {@link io.netty.channel.ChannelPipeline}. Sub-classes may override a method implementation to change this. * * * I/O? * * I/O ? ?DB * * EVENT_EXECUTORS ?? ServerHandler * ? ? * * * * channel1.?ChannelRead()inactive?buf * 2.inactive??? RecyclableArrayList buf ChannelRead() * * 3. ?channel RecyclableArrayListchannel? * * */ public class ServerHandler extends ChannelInboundHandlerAdapter { public static final Logger logger = LoggerFactory.getLogger(ServerHandler.class); /**?--*/ private static final AtomicInteger countConnection = new AtomicInteger(0); public static BeanFactory springContext; //spring context manager by spring private PlayerInstance player; public ServerHandler() { } /** * ???? * Calls {@link io.netty.channel.ChannelHandlerContext#fireChannelActive()} to forward * to the next {@link io.netty.channel.ChannelInboundHandler} in the {@link io.netty.channel.ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { countConnection.getAndIncrement();// +1 player = (PlayerInstance) ServerHandler.springContext.getBean("player"); // player.setChannel(ctx.channel()); if (logger.isDebugEnabled()) logger.debug(ctx.channel() + "connect server... Concurrent connection... " + countConnection.get()); } /** * Calls {@link io.netty.channel.ChannelHandlerContext#fireChannelRead(Object)} to forward * to the next {@link io.netty.channel.ChannelInboundHandler} in the {@link io.netty.channel.ChannelPipeline}. * ?channel RecyclableArrayListchannel? */ @Override public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception { ByteBuf msg = (ByteBuf) message; /** * if channel inactive ,the finally callback method{@link io.netty.handler.codec.ByteToMessageDecoder#channelInactive(io.netty.channel.ChannelHandlerContext)} * this method will do some clear work ,if Recycle ArrayList is not empty ,so will invoke method channelRead,and release byteBuffer, * every task(byteBuffer data which was store blockingQueue will be executor by the "" ,no matter what it's status) , * so if the channel inactive ,we will clear some buf ,in case of memory crash application,like this : */ if (!ctx.channel().isActive()) { //inactive status , release buffer msg.release(); msg = null; } else { try { /**ID**/ int ID = msg.readInt(); /**byte[]pb?**/ int length = msg.readableBytes(); if (length != 0) { byte[] array = new byte[length]; msg.getBytes(msg.readerIndex(), array, 0, length); msg.release(); //release byte buffer msg = null; //collection by GC directly MessageLite messageLite = IController.MESSAGE_LITE[ID].getParserForType().parseFrom(array, 0, length, ExtensionRegistryLite.getEmptyRegistry()); array = null; //collection by GC directly logger.debug("? &( ^___^ )& -->" + messageLite.toString() + "ID:" + ID); IController.GAME_CONTROLLERS[ID].DispatchMessageLit(messageLite, player); } else { msg.release(); msg = null; /**?null*/ IController.GAME_CONTROLLERS[ID].DispatchMessageLit(null, player); } } catch (UninitializedMessageException e) { e.printStackTrace(); } } } /** * Calls {@link io.netty.channel.ChannelHandlerContext#fireChannelInactive()} to forward * to the next {@link io.netty.channel.Channel} in the {@link io.netty.channel.ChannelPipeline}. * * Sub-classes may override this method to change behavior. * * ?? */ @Override public void channelInactive(ChannelHandlerContext ctx) { try { ctx.channel().close(); //clear and store data! channel closed! // this.player.saveAndDestory(); countConnection.decrementAndGet();// -1 if (logger.isDebugEnabled()) logger.debug(ctx.channel().remoteAddress() + " channelClosed , Concurrent connection... " + countConnection.get()); ChannelPipeline channelPipeline = ctx.channel().pipeline(); if (null == channelPipeline) { // if null ,will be returned return; } //remove pipeline object while (channelPipeline.last() != null) { channelPipeline.removeLast(); } if (ctx.isRemoved()) { ctx.close(); // Set to null so the GC can collect them directly channelPipeline = null; ctx = null; player = null; } } catch (NoSuchElementException ee) { //do nothing } catch (Exception e) { if (logger.isDebugEnabled()) logger.error("", e); //do nothing } } /** * ? * ? * @param ctx * @param evt * @throws Exception */ @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { Channel channel = ctx.channel(); if (evt instanceof IdleStateEvent) { IdleStateEvent e = (IdleStateEvent) evt; //No data was received for a while. if (e.state() == IdleState.READER_IDLE) { //you can do sth ,such as timeout channel.close(); //call back channelInactive(ChannelHandlerContext ctx) if (logger.isDebugEnabled()) logger.debug( channel.remoteAddress() + "---No data was received for a while ,read time out... ..."); } // because we are attaching more importance to the read_idle time(timeout to rec) else if (e.state() == IdleState.WRITER_IDLE) { // No data was sent for a while. channel.close(); if (logger.isDebugEnabled()) logger.debug(channel.remoteAddress() + "---No data was sent for a while.write time out... ..."); } } } /** * ? * @return player num */ public static int getOnlinePlayers() { return countConnection.get(); } /** * Sub-classes may override this method to change behavior. */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); // try { //// ctx.channel().close(); //// ctx.close(); // }catch (Exception e){ // e.printStackTrace(); // } } /**spring context**/ public static void setSpringContext(BeanFactory springContext) { ServerHandler.springContext = springContext; } }