package com.forest.ape.nio; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelHandler; import org.jboss.netty.channel.WriteCompletionEvent; import org.jboss.netty.channel.ChannelHandler.Sharable; import org.jboss.netty.channel.group.ChannelGroup; import org.jboss.netty.channel.group.DefaultChannelGroup; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.forest.ape.server.ApeServer; import com.forest.ape.server.Server; /** * * @author CHQ 2012-2-8 */ public class NettyServerCnxnFactory extends ServerCnxnFactory { Logger LOG = LoggerFactory.getLogger(NettyServerCnxnFactory.class); /** * we can know how many clients is connected now */ ChannelGroup allChannels = new DefaultChannelGroup("ApeChinnels"); ServerBootstrap bootstrap; Channel parentChannel; CnxnChannelHandler channelHandler = new CnxnChannelHandler(); Server server; HashSet<ServerCnxn> cnxns = new HashSet<ServerCnxn>(); HashMap<InetAddress, Set<NettyServerCnxn>> ipMap = new HashMap<InetAddress, Set<NettyServerCnxn>>(); InetSocketAddress localAddress; int maxClientCnxns = 60; AtomicLong sessionCnt = new AtomicLong(0); @Sharable class CnxnChannelHandler extends SimpleChannelHandler { @Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("Channel closed " + e); } allChannels.remove(ctx.getChannel()); } @Override public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("Channel connected " + e); } allChannels.add(ctx.getChannel()); NettyServerCnxn cnxn = new NettyServerCnxn(ctx.getChannel(), server, NettyServerCnxnFactory.this); cnxn.setSessionId(sessionCnt.addAndGet(1)); ctx.setAttachment(cnxn); addCnxn(cnxn); } @Override public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("Channel disconnected " + e); } NettyServerCnxn cnxn = (NettyServerCnxn) ctx.getAttachment(); if (cnxn != null) { if (LOG.isTraceEnabled()) { LOG.trace("Channel disconnect caused close " + e); } cnxn.close(); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { LOG.warn("Exception caught " + e, e.getCause()); NettyServerCnxn cnxn = (NettyServerCnxn) ctx.getAttachment(); if (cnxn != null) { if (LOG.isDebugEnabled()) { LOG.debug("Closing " + cnxn); cnxn.close(); } } } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("message received called " + e.getMessage()); } try { if (LOG.isDebugEnabled()) { LOG.debug("New message " + e.toString() + " from " + ctx.getChannel()); } NettyServerCnxn cnxn = (NettyServerCnxn) ctx.getAttachment(); synchronized (cnxn) { cnxn.receiveMessage((ChannelBuffer) e.getMessage()); } } catch (Exception ex) { LOG.error("Unexpected exception in receive", ex); throw ex; } } ByteBuffer bb = null; private void processMessage(MessageEvent e, NettyServerCnxn cnxn) { } private void increasePacketReceived() { // TODO Auto-generated method stub } @Override public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) throws Exception { if (LOG.isTraceEnabled()) { LOG.trace("write complete " + e); } } } NettyServerCnxnFactory() { bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // parent channel bootstrap.setOption("reuseAddress", true); // child channels bootstrap.setOption("child.tcpNoDelay", true); bootstrap.getPipeline().addLast("channelHandle", channelHandler); } public static void main(String[] args) { new NettyServerCnxnFactory(); } boolean killed; @Override public void join() throws InterruptedException { synchronized (this) { while (!killed) { wait(); } } } @Override public void shutdown() { LOG.info("shutdown called " + localAddress); // null if factory never started if (parentChannel != null) { parentChannel.close().awaitUninterruptibly(); closeAll(); allChannels.close().awaitUninterruptibly(); bootstrap.releaseExternalResources(); } if (server != null) { server.shutdown(); } synchronized (this) { killed = true; notifyAll(); } } @Override public void start() { LOG.info("binding to port " + localAddress); parentChannel = bootstrap.bind(localAddress); } @Override public void configure(InetSocketAddress addr, int maxClientCnxns) throws IOException { this.localAddress = addr; this.maxClientCnxns = maxClientCnxns; } @Override public int getMaxClientCnxnsPerHost() { // TODO Auto-generated method stub return 0; } @Override public void setMaxClientCnxnsPerHost(int max) { // TODO Auto-generated method stub } @Override public void closeAll() { if (LOG.isDebugEnabled()) { LOG.debug("closeAll()"); } synchronized (cnxns) { // got to clear all the connections that we have in the selector for (NettyServerCnxn cnxn : cnxns.toArray(new NettyServerCnxn[cnxns .size()])) { try { cnxn.close(); } catch (Exception e) { LOG.warn("Ignoring exception closing cnxn sessionid 0x" + Long.toHexString(cnxn.getSessionId()), e); } } } if (LOG.isDebugEnabled()) { LOG.debug("allChannels size:" + allChannels.size() + " cnxns size:" + cnxns.size()); } } @Override public InetSocketAddress getLocalAddress() { // TODO Auto-generated method stub return null; } public Server getServer() { return server; } public void setServer(Server server) { this.server = server; } private void addCnxn(NettyServerCnxn cnxn) { synchronized (cnxns) { cnxns.add(cnxn); synchronized (ipMap) { InetAddress addr = ((InetSocketAddress) cnxn.channel .getRemoteAddress()).getAddress(); Set<NettyServerCnxn> s = ipMap.get(addr); if (s == null) { s = new HashSet<NettyServerCnxn>(); } s.add(cnxn); ipMap.put(addr, s); } } } void removeCnxn(NettyServerCnxn cnxn) { synchronized (cnxns) { boolean isOK = cnxns.remove(cnxn); if (isOK) LOG.debug("remove from cnxns and ipmap"); synchronized (ipMap) { InetAddress addr = ((InetSocketAddress) cnxn.channel .getRemoteAddress()).getAddress(); Set<NettyServerCnxn> s = ipMap.get(addr); s.remove(cnxn); ipMap.put(addr, s); } } } }