/** * Method parseClientPacket. * * @param handler IPacketHandler<T> * @param buf ByteBuffer * @param dataSize int * @param con MMOConnection<T> * @return boolean */ protected boolean parseClientPacket( IPacketHandler<T> handler, ByteBuffer buf, int dataSize, MMOConnection<T> con) { T client = con.getClient(); int pos = buf.position(); client.decrypt(buf, dataSize); buf.position(pos); if (buf.hasRemaining()) { // apply limit int limit = buf.limit(); buf.limit(pos + dataSize); ReceivablePacket<T> rp = handler.handlePacket(buf, client); if (rp != null) { rp.setByteBuffer(buf); rp.setClient(client); if (rp.read()) { con.recvPacket(rp); } rp.setByteBuffer(null); } buf.limit(limit); } return true; }
/** * Method acceptConnection. * * @param key SelectionKey */ protected void acceptConnection(SelectionKey key) { ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); SocketChannel sc; SelectionKey clientKey; try { while ((sc = ssc.accept()) != null) { if ((getAcceptFilter() == null) || getAcceptFilter().accept(sc)) { sc.configureBlocking(false); clientKey = sc.register(getSelector(), SelectionKey.OP_READ); MMOConnection<T> con = new MMOConnection<>(this, sc.socket(), clientKey); T client = getClientFactory().create(con); client.setConnection(con); con.setClient(client); clientKey.attach(con); _connections.add(con); stats.increaseOpenedConnections(); } else { sc.close(); } } } catch (IOException e) { _log.error("Error in " + getName(), e); } }
/** * Method tryReadPacket2. * * @param key SelectionKey * @param con MMOConnection<T> * @param buf ByteBuffer * @return boolean */ protected boolean tryReadPacket2(SelectionKey key, MMOConnection<T> con, ByteBuffer buf) { if (con.isClosed()) { return false; } int pos = buf.position(); if (buf.remaining() > _sc.HEADER_SIZE) { int size = buf.getShort() & 0xffff; if ((size <= _sc.HEADER_SIZE) || (size > _sc.PACKET_SIZE)) { _log.error( "Incorrect packet size : " + size + "! Client : " + con.getClient() + ". Closing connection."); closeConnectionImpl(con); return false; } size -= _sc.HEADER_SIZE; if (size <= buf.remaining()) { stats.increaseIncomingPacketsCount(); parseClientPacket(getPacketHandler(), buf, size, con); buf.position(pos + size + _sc.HEADER_SIZE); if (!buf.hasRemaining()) { freeBuffer(buf, con); return false; } return true; } buf.position(pos); } if (pos == buf.capacity()) { _log.warn( "Read buffer exhausted for client : " + con.getClient() + ", try to adjust buffer size, current : " + buf.capacity() + ", primary : " + (buf == READ_BUFFER) + "."); } if (buf == READ_BUFFER) { allocateReadBuffer(con); } else { buf.compact(); } return false; }
/** * Method finishConnection. * * @param key SelectionKey */ protected void finishConnection(SelectionKey key) { try { ((SocketChannel) key.channel()).finishConnect(); } catch (IOException e) { MMOConnection<T> con = (MMOConnection<T>) key.attachment(); T client = con.getClient(); client.getConnection().onForcedDisconnection(); closeConnectionImpl(client.getConnection()); } }
/** * Method freeBuffer. * * @param buf ByteBuffer * @param con MMOConnection<T> */ protected void freeBuffer(ByteBuffer buf, MMOConnection<T> con) { if (buf == READ_BUFFER) { READ_BUFFER.clear(); } else { con.setReadBuffer(null); recycleBuffer(buf); } }
/** * Method writePacket. * * @param key SelectionKey */ protected void writePacket(SelectionKey key) { MMOConnection<T> con = (MMOConnection<T>) key.attachment(); prepareWriteBuffer(con); DIRECT_WRITE_BUFFER.flip(); int size = DIRECT_WRITE_BUFFER.remaining(); int result = -1; try { result = con.getWritableChannel().write(DIRECT_WRITE_BUFFER); } catch (IOException e) { // error handling goes on the if bellow } // check if no error happened if (result >= 0) { stats.increaseOutgoingBytes(result); // check if we written everything if (result != size) { con.createWriteBuffer(DIRECT_WRITE_BUFFER); } if (!con.getSendQueue().isEmpty() || con.hasPendingWriteBuffer()) { con.scheduleWriteInterest(); } } else { con.onForcedDisconnection(); closeConnectionImpl(con); } }
/** * Method readPacket. * * @param key SelectionKey */ protected void readPacket(SelectionKey key) { MMOConnection<T> con = (MMOConnection<T>) key.attachment(); if (con.isClosed()) { return; } ByteBuffer buf; int result = -2; if ((buf = con.getReadBuffer()) == null) { buf = READ_BUFFER; } // if we try to to do a read with no space in the buffer it will read 0 bytes // going into infinite loop if (buf.position() == buf.limit()) { _log.error( "Read buffer exhausted for client : " + con.getClient() + ", try to adjust buffer size, current : " + buf.capacity() + ", primary : " + (buf == READ_BUFFER) + ". Closing connection."); closeConnectionImpl(con); } else { try { result = con.getReadableByteChannel().read(buf); } catch (IOException e) { // error handling goes bellow } if (result > 0) { buf.flip(); stats.increaseIncomingBytes(result); @SuppressWarnings("unused") int i; for (i = 0; this.tryReadPacket2(key, con, buf); i++) {} } else if (result == 0) { closeConnectionImpl(con); } else if (result == -1) { closeConnectionImpl(con); } else { con.onForcedDisconnection(); closeConnectionImpl(con); } } if (buf == READ_BUFFER) { buf.clear(); } }
/** * Method prepareWriteBuffer. * * @param con MMOConnection<T> */ protected void prepareWriteBuffer(MMOConnection<T> con) { WRITE_CLIENT = con.getClient(); DIRECT_WRITE_BUFFER.clear(); if (con.hasPendingWriteBuffer()) { con.movePendingWriteBufferTo(DIRECT_WRITE_BUFFER); } if (DIRECT_WRITE_BUFFER.hasRemaining() && !con.hasPendingWriteBuffer()) { int i; Queue<SendablePacket<T>> sendQueue = con.getSendQueue(); SendablePacket<T> sp; for (i = 0; i < _sc.MAX_SEND_PER_PASS; i++) { synchronized (con) { if ((sp = sendQueue.poll()) == null) { break; } } try { stats.increaseOutgoingPacketsCount(); putPacketIntoWriteBuffer(sp, true); WRITE_BUFFER.flip(); if (DIRECT_WRITE_BUFFER.remaining() >= WRITE_BUFFER.limit()) { DIRECT_WRITE_BUFFER.put(WRITE_BUFFER); } else { con.createWriteBuffer(WRITE_BUFFER); break; } } catch (Exception e) { _log.error("Error in " + getName(), e); break; } } } WRITE_BUFFER.clear(); WRITE_CLIENT = null; }
/** * Method closeConnectionImpl. * * @param con MMOConnection<T> */ protected void closeConnectionImpl(MMOConnection<T> con) { try { // notify connection con.onDisconnection(); } finally { try { // close socket and the SocketChannel con.close(); } catch (IOException e) { // ignore, we are closing anyway } finally { con.releaseBuffers(); con.clearQueues(); con.getClient().setConnection(null); con.getSelectionKey().attach(null); con.getSelectionKey().cancel(); _connections.remove(con); stats.decreseOpenedConnections(); } } }
/** * Method allocateReadBuffer. * * @param con MMOConnection<T> */ protected void allocateReadBuffer(MMOConnection<T> con) { con.setReadBuffer(getPooledBuffer().put(READ_BUFFER)); READ_BUFFER.clear(); }
/** * Method run. * * @see java.lang.Runnable#run() */ @Override public void run() { int totalKeys = 0; Set<SelectionKey> keys = null; Iterator<SelectionKey> itr = null; Iterator<MMOConnection<T>> conItr = null; SelectionKey key = null; MMOConnection<T> con = null; long currentMillis = 0L; // main loop for (; ; ) { try { if (isShuttingDown()) { closeSelectorThread(); break; } currentMillis = System.currentTimeMillis(); conItr = _connections.iterator(); while (conItr.hasNext()) { con = conItr.next(); if (con.isPengingClose()) { if (!con.isPendingWrite() || ((currentMillis - con.getPendingCloseTime()) >= 10000L)) { closeConnectionImpl(con); continue; } } if (con.isPendingWrite()) { if ((currentMillis - con.getPendingWriteTime()) >= _sc.INTEREST_DELAY) { con.enableWriteInterest(); } } } totalKeys = getSelector().selectNow(); if (totalKeys > 0) { keys = getSelector().selectedKeys(); itr = keys.iterator(); while (itr.hasNext()) { key = itr.next(); itr.remove(); if (key.isValid()) { try { if (key.isAcceptable()) { acceptConnection(key); continue; } else if (key.isConnectable()) { finishConnection(key); continue; } if (key.isReadable()) { readPacket(key); } if (key.isValid()) { if (key.isWritable()) { writePacket(key); } } } catch (CancelledKeyException cke) { } } } } try { Thread.sleep(_sc.SLEEP_TIME); } catch (InterruptedException ie) { } } catch (IOException e) { _log.error("Error in " + getName(), e); try { Thread.sleep(1000L); } catch (InterruptedException ie) { } } } }