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.l2jfree.mmocore.network; import java.io.IOException; import java.net.InetAddress; import java.nio.channels.ClosedChannelException; import java.nio.channels.SocketChannel; import org.apache.commons.logging.Log; import com.l2jfree.mmocore.network.FloodManager.ErrorMode; import com.l2jfree.mmocore.network.FloodManager.Result; /** * @author KenM<BR> * Parts of design based on networkcore from WoodenGil */ public abstract class SelectorThread<T extends MMOConnection<T, RP, SP>, RP extends ReceivablePacket<T, RP, SP>, SP extends SendablePacket<T, RP, SP>> { protected static final Log _log = new MMOLogger(SelectorThread.class, 1000); private final AcceptorThread<T, RP, SP> _acceptorThread; private final ReadWriteThread<T, RP, SP>[] _readWriteThreads; @SuppressWarnings("unchecked") protected SelectorThread(SelectorConfig sc, IPacketHandler<T, RP, SP> packetHandler) throws IOException { _acceptorThread = new AcceptorThread<T, RP, SP>("AcceptorThread", this, sc); _readWriteThreads = new ReadWriteThread[sc.getSelectorThreadCount()]; for (int i = 0; i < _readWriteThreads.length; i++) _readWriteThreads[i] = new ReadWriteThread<T, RP, SP>("ReadWriteThread-" + (i + 1), this, sc, packetHandler); } public final void openServerSocket(String address, int port) throws IOException { openServerSocket(InetAddress.getByName(address), port); } public final void openServerSocket(InetAddress address, int port) throws IOException { getAcceptorThread().openServerSocket(address, port); } private AcceptorThread<T, RP, SP> getAcceptorThread() { return _acceptorThread; } private ReadWriteThread<T, RP, SP>[] getReadWriteThreads() { return _readWriteThreads; } private int _readWriteThreadIndex; final ReadWriteThread<T, RP, SP> getReadWriteThread() { return getReadWriteThreads()[_readWriteThreadIndex++ % getReadWriteThreads().length]; } public final void start() { getAcceptorThread().start(); for (ReadWriteThread<T, RP, SP> readWriteThread : getReadWriteThreads()) readWriteThread.start(); } public final void shutdown() throws InterruptedException { getAcceptorThread().shutdown(); for (ReadWriteThread<T, RP, SP> readWriteThread : getReadWriteThreads()) readWriteThread.shutdown(); } // ============================================== protected abstract T createClient(SocketChannel socketChannel) throws ClosedChannelException; protected abstract void executePacket(RP packet); // ============================================== private final FloodManager _accepts; private final FloodManager _packets; private final FloodManager _errors; { // TODO: fine tune _accepts = new FloodManager(1000, // 1000 msec per tick new FloodManager.FloodFilter(10, 20, 10), // short period new FloodManager.FloodFilter(30, 60, 60)); // long period _packets = new FloodManager(1000, // 1000 msec per tick new FloodManager.FloodFilter(250, 300, 2)); _errors = new FloodManager(200, // 200 msec per tick new FloodManager.FloodFilter(10, 10, 1)); } protected String getVersionInfo() { return ""; } protected boolean acceptConnectionFrom(SocketChannel sc) { final String host = sc.socket().getInetAddress().getHostAddress(); final Result isFlooding = _accepts.isFlooding(host, true); switch (isFlooding) { case REJECTED: { // TODO: punish, warn, log, etc _log.warn("Rejected connection from " + host); return false; } case WARNED: { // TODO: punish, warn, log, etc _log.warn("Connection over warn limit from " + host); return true; } default: return true; } } public void report(ErrorMode mode, T client, RP packet, Throwable throwable) { final Result isFlooding = _errors.isFlooding(client.getValidUID(), true); final StringBuilder sb = new StringBuilder(); if (isFlooding != Result.ACCEPTED) { sb.append("Flooding with "); } sb.append(mode); sb.append(": "); sb.append(client); if (packet != null) { sb.append(" - "); sb.append(packet.getType()); } final String versionInfo = getVersionInfo(); if (versionInfo != null && !versionInfo.isEmpty()) { sb.append(" - "); sb.append(versionInfo); } if (throwable != null) _log.info(sb, throwable); else _log.info(sb); //if (isFlooding != Result.ACCEPTED) //{ // // TODO: punish, warn, log, etc //} } protected boolean canReceivePacketFrom(T client, int opcode) { final String key = client.getValidUID(); switch (Result.max(_packets.isFlooding(key, true), _errors.isFlooding(key, false))) { case REJECTED: { // TODO: punish, warn, log, etc _log.warn("Rejected packet (0x" + Integer.toHexString(opcode) + ") from " + client); return false; } case WARNED: { // TODO: punish, warn, log, etc _log.warn("Packet over warn limit (0x" + Integer.toHexString(opcode) + ") from " + client); return true; } default: return true; } } }