Java tutorial
/* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package com.tera.common.network.nio; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import javolution.util.FastList; import org.apache.commons.lang.NotImplementedException; import com.tera.common.idfactory.IdFactory; import com.tera.common.idfactory.SimpleIdFactory; import com.tera.common.model.callback.CDeferredCallbackHandler; import com.tera.common.model.callback.DeferredCallbackHandler; import com.tera.common.network.model.AbstractNetworkChannel; import com.tera.common.network.model.ChannelHandler; import com.tera.common.network.model.ChannelState; import com.tera.common.network.packet.NetworkPacket; import com.tera.common.network.packet.PacketService; /** * @author KenM * @modified ATracer */ public abstract class MMOConnection extends AbstractNetworkChannel implements ChannelHandler { private final SelectorThread selectorThread; private final ReadWriteThread readWriteThread; private final Socket socket; private InetAddress inetAddress; private String hostAddress; private FastList<NetworkPacket> sendQueue; private final SelectionKey selectionKey; private ByteBuffer readBuffer; private ByteBuffer primaryWriteBuffer; private ByteBuffer secondaryWriteBuffer; private long closeTimeout = -1; private DeferredCallbackHandler<MMOConnection> closeListeners = new CDeferredCallbackHandler<MMOConnection>(); private static IdFactory idFactory = new SimpleIdFactory(); private final int connectionId; protected MMOConnection(PacketService packetService, SelectorThread selectorThread, SocketChannel socketChannel) throws ClosedChannelException { super(packetService); this.selectorThread = selectorThread; this.readWriteThread = getSelectorThread().getReadWriteThread(); this.socket = socketChannel.socket(); this.inetAddress = socket.getInetAddress(); this.hostAddress = inetAddress.getHostAddress(); this.selectionKey = socketChannel.register(getReadWriteThread().getSelector(), SelectionKey.OP_READ); this.selectionKey.attach(this); setChannelState(ChannelState.CONNECTED); connectionId = idFactory.nextId();//TODO deallocate after disconnect } @Override public Object getChannel() { return getSocketChannel(); } @Override public ChannelHandler getHandler() { throw new NotImplementedException(); } @Override public final boolean isConnected() { return !isClosed();// temporary } @Override public final int getChannelId() { return connectionId; } final boolean closeTimeouted() { return System.currentTimeMillis() > closeTimeout; } @Override public synchronized void write(NetworkPacket networkPacket) { if (isClosed()) return; try { getSelectionKey().interestOps(getSelectionKey().interestOps() | SelectionKey.OP_WRITE); getSendQueue2().addLast(networkPacket); } catch (CancelledKeyException e) { // ignore } } @Override public synchronized void writeAndClose(NetworkPacket networkPacket) { if (isClosed()) return; getSendQueue2().clear(); write(networkPacket); // let the client have the chance to get all of the packets // even for a connection with issues 10'000 msec should be enough to write // the pending packets closeTimeout = System.currentTimeMillis() + 10000; disableReadInterest(); getReadWriteThread().closeConnection(this); } @Override public synchronized void close() { if (isClosed()) return; getSendQueue2().clear(); // close the client as soon as possible // for a normal connection 100 msec should be enough to write the pending // packets closeTimeout = System.currentTimeMillis() + 100; disableReadInterest(); getReadWriteThread().closeConnection(this); } /** * Notify all close listeners that connection was closed. This event will be * received after all necessary connection resources was cleaned by worker * thread. * * @param forced */ public void notifyConnectionClosed(boolean forced) { closeListeners.operationComplete(this); } @Override public InetSocketAddress getAddress() { return (InetSocketAddress) socket.getRemoteSocketAddress(); } @Override public String getIpAddress() { return hostAddress; } final SelectorThread getSelectorThread() { return selectorThread; } private ReadWriteThread getReadWriteThread() { return readWriteThread; } final SelectionKey getSelectionKey() { return selectionKey; } final void disableReadInterest() { try { getSelectionKey().interestOps(getSelectionKey().interestOps() & ~SelectionKey.OP_READ); } catch (CancelledKeyException e) { // ignore } } final void disableWriteInterest() { try { getSelectionKey().interestOps(getSelectionKey().interestOps() & ~SelectionKey.OP_WRITE); } catch (CancelledKeyException e) { // ignore } } final Socket getSocket() { return socket; } final SocketChannel getSocketChannel() { return socket.getChannel(); } final FastList<NetworkPacket> getSendQueue2() { if (sendQueue == null) sendQueue = new FastList<NetworkPacket>(); return sendQueue; } final void createWriteBuffer(ByteBuffer buf) { if (primaryWriteBuffer == null) { // APPENDING FOR NULL primaryWriteBuffer = getReadWriteThread().getPooledBuffer(); primaryWriteBuffer.put(buf); } else { // PREPENDING ON EXISTING ByteBuffer temp = getReadWriteThread().getPooledBuffer(); temp.put(buf); int remaining = temp.remaining(); primaryWriteBuffer.flip(); int limit = primaryWriteBuffer.limit(); if (remaining >= primaryWriteBuffer.remaining()) { temp.put(primaryWriteBuffer); getReadWriteThread().recycleBuffer(primaryWriteBuffer); primaryWriteBuffer = temp; } else { primaryWriteBuffer.limit(remaining); temp.put(primaryWriteBuffer); primaryWriteBuffer.limit(limit); primaryWriteBuffer.compact(); secondaryWriteBuffer = primaryWriteBuffer; primaryWriteBuffer = temp; } } } final boolean hasPendingWriteBuffer() { return primaryWriteBuffer != null; } final void movePendingWriteBufferTo(ByteBuffer dest) { primaryWriteBuffer.flip(); dest.put(primaryWriteBuffer); getReadWriteThread().recycleBuffer(primaryWriteBuffer); primaryWriteBuffer = secondaryWriteBuffer; secondaryWriteBuffer = null; } final void setReadBuffer(ByteBuffer buf) { readBuffer = buf; } final ByteBuffer getReadBuffer() { return readBuffer; } final boolean isClosed() { return closeTimeout != -1; } final void releaseBuffers() { if (primaryWriteBuffer != null) { getReadWriteThread().recycleBuffer(primaryWriteBuffer); primaryWriteBuffer = null; if (secondaryWriteBuffer != null) { getReadWriteThread().recycleBuffer(secondaryWriteBuffer); secondaryWriteBuffer = null; } } if (readBuffer != null) { getReadWriteThread().recycleBuffer(readBuffer); readBuffer = null; } } protected abstract boolean decrypt(ByteBuffer buf, int size); protected abstract void encrypt(ByteBuffer buf, int size); }