PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Commons/src/com/aionemu/commons/network/Dispatcher.java

http://aionxemu.googlecode.com/
Java | 343 lines | 170 code | 38 blank | 135 comment | 29 complexity | 49c9bcf40f2c951d0751bfaeebab0869 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, BSD-2-Clause
  1. /**
  2. * This file is part of Aion X Emu <aionxemu.com>
  3. *
  4. * This is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU Lesser Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Lesser Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Lesser Public License
  15. * along with this software. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. package com.aionemu.commons.network;
  18. import com.aionemu.commons.options.Assertion;
  19. import org.apache.log4j.Logger;
  20. import java.io.IOException;
  21. import java.nio.ByteBuffer;
  22. import java.nio.ByteOrder;
  23. import java.nio.channels.SelectableChannel;
  24. import java.nio.channels.SelectionKey;
  25. import java.nio.channels.Selector;
  26. import java.nio.channels.SocketChannel;
  27. import java.nio.channels.spi.SelectorProvider;
  28. /**
  29. * Dispatcher that dispatch SelectionKeys set selected by Selector.
  30. *
  31. * @author -Nemesiss-
  32. */
  33. public abstract class Dispatcher extends Thread {
  34. /**
  35. * Logger for Dispatcher
  36. */
  37. private static final Logger log = Logger.getLogger(Dispatcher.class);
  38. /**
  39. * Selector thats selecting ready keys.
  40. */
  41. Selector selector;
  42. /**
  43. * ThreadPool on witch disconnection tasks will be executed.
  44. */
  45. private final DisconnectionThreadPool dcPool;
  46. /**
  47. * Object on witch register vs selector.select are synchronized
  48. */
  49. private final Object gate = new Object();
  50. /**
  51. * Constructor.
  52. *
  53. * @param name
  54. * @param dcPool
  55. * @throws IOException
  56. */
  57. public Dispatcher(String name, DisconnectionThreadPool dcPool) throws IOException {
  58. super(name);
  59. this.selector = SelectorProvider.provider().openSelector();
  60. this.dcPool = dcPool;
  61. }
  62. /**
  63. * Add connection to pendingClose list, so this connection will be closed by this <code>Dispatcher</code> as soon as
  64. * possible.
  65. *
  66. * @param con
  67. * @see com.aionemu.commons.network.Dispatcher#closeConnection(com.aionemu.commons.network.AConnection)
  68. */
  69. abstract void closeConnection(AConnection con);
  70. /**
  71. * Dispatch Selected keys and process pending close.
  72. *
  73. * @throws IOException
  74. */
  75. abstract void dispatch() throws IOException;
  76. /**
  77. * @return Selector of this Dispatcher
  78. */
  79. public final Selector selector() {
  80. return this.selector;
  81. }
  82. /**
  83. * Dispatching Selected keys and processing pending close.
  84. *
  85. * @see java.lang.Thread#run()
  86. */
  87. @Override
  88. public void run() {
  89. for (; ;) {
  90. try {
  91. dispatch();
  92. synchronized (gate) {
  93. }
  94. }
  95. catch (Exception e) {
  96. log.error("Dispatcher error! " + e, e);
  97. }
  98. }
  99. }
  100. /**
  101. * Register new client connected to this Dispatcher and set SelectionKey (result of registration) as this key of
  102. * given AConnection.
  103. *
  104. * @param ch
  105. * @param ops
  106. * @param att
  107. * @throws IOException
  108. */
  109. public final void register(SelectableChannel ch, int ops, AConnection att) throws IOException {
  110. synchronized (gate) {
  111. selector.wakeup();
  112. att.setKey(ch.register(selector, ops, att));
  113. }
  114. }
  115. /**
  116. * Register new Acceptor this Dispatcher and return SelectionKey (result of registration).
  117. *
  118. * @param ch
  119. * @param ops
  120. * @param att
  121. * @return SelectionKey representing this registration.
  122. * @throws IOException
  123. */
  124. public final SelectionKey register(SelectableChannel ch, int ops, Acceptor att) throws IOException {
  125. synchronized (gate) {
  126. selector.wakeup();
  127. return ch.register(selector, ops, att);
  128. }
  129. }
  130. /**
  131. * Accept new connection.
  132. *
  133. * @param key
  134. */
  135. final void accept(SelectionKey key) {
  136. try {
  137. Acceptor acceptor = (Acceptor) key.attachment();
  138. if (acceptor == null)
  139. return;
  140. acceptor.accept(key);
  141. }
  142. catch (Exception e) {
  143. log.error("Error while accepting connection: +" + e, e);
  144. }
  145. }
  146. /**
  147. * Read data from socketChannel represented by SelectionKey key. Parse and Process data. Prepare buffer for next
  148. * read.
  149. *
  150. * @param key
  151. */
  152. final void read(SelectionKey key) {
  153. SocketChannel socketChannel = (SocketChannel) key.channel();
  154. AConnection con = (AConnection) key.attachment();
  155. ByteBuffer rb = con.readBuffer;
  156. /**
  157. * Test if this build should use assertion. If NetworkAssertion == false javac will remove this code block
  158. */
  159. if (Assertion.NetworkAssertion) {
  160. assert con.readBuffer.hasRemaining();
  161. }
  162. /** Attempt to read off the channel */
  163. int numRead;
  164. try {
  165. numRead = socketChannel.read(rb);
  166. }
  167. catch (IOException e) {
  168. closeConnectionImpl(con);
  169. return;
  170. }
  171. if (numRead == -1) {
  172. /**
  173. * Remote entity shut the socket down cleanly. Do the same from our end and cancel the channel.
  174. */
  175. closeConnectionImpl(con);
  176. return;
  177. } else if (numRead == 0) {
  178. return;
  179. }
  180. rb.flip();
  181. while (rb.remaining() > 2 && rb.remaining() >= rb.getShort(rb.position())) {
  182. /** got full message */
  183. if (!parse(con, rb)) {
  184. closeConnectionImpl(con);
  185. return;
  186. }
  187. }
  188. if (rb.hasRemaining()) {
  189. con.readBuffer.compact();
  190. /**
  191. * Test if this build should use assertion. If NetworkAssertion == false javac will remove this code block
  192. */
  193. if (Assertion.NetworkAssertion) {
  194. assert con.readBuffer.hasRemaining();
  195. }
  196. } else
  197. rb.clear();
  198. }
  199. /**
  200. * Parse data from buffer and prepare buffer for reading just one packet - call processData(ByteBuffer b).
  201. *
  202. * @param con Connection
  203. * @param buf Buffer with packet data
  204. * @return True if packet was parsed.
  205. */
  206. private boolean parse(AConnection con, ByteBuffer buf) {
  207. short sz = 0;
  208. try {
  209. sz = buf.getShort();
  210. if (sz > 1)
  211. sz -= 2;
  212. ByteBuffer b = (ByteBuffer) buf.slice().limit(sz);
  213. b.order(ByteOrder.LITTLE_ENDIAN);
  214. /** read message fully */
  215. buf.position(buf.position() + sz);
  216. return con.processData(b);
  217. }
  218. catch (IllegalArgumentException e) {
  219. log.warn("Error on parsing input from client - account: " + con + " packet size: " + sz + " real size:"
  220. + buf.remaining(), e);
  221. return false;
  222. }
  223. }
  224. /**
  225. * Write as much as possible data to socketChannel represented by SelectionKey key. If all data were written key
  226. * write interest will be disabled.
  227. *
  228. * @param key
  229. */
  230. final void write(SelectionKey key) {
  231. SocketChannel socketChannel = (SocketChannel) key.channel();
  232. AConnection con = (AConnection) key.attachment();
  233. int numWrite;
  234. ByteBuffer wb = con.writeBuffer;
  235. /** We have not writted data */
  236. if (wb.hasRemaining()) {
  237. try {
  238. numWrite = socketChannel.write(wb);
  239. }
  240. catch (IOException e) {
  241. closeConnectionImpl(con);
  242. return;
  243. }
  244. if (numWrite == 0) {
  245. log.info("Write " + numWrite + " ip: " + con.getIP());
  246. return;
  247. }
  248. /** Again not all data was send */
  249. if (wb.hasRemaining())
  250. return;
  251. }
  252. while (true) {
  253. wb.clear();
  254. boolean writeFailed = !con.writeData(wb);
  255. if (writeFailed) {
  256. wb.limit(0);
  257. break;
  258. }
  259. /** Attempt to write to the channel */
  260. try {
  261. numWrite = socketChannel.write(wb);
  262. }
  263. catch (IOException e) {
  264. closeConnectionImpl(con);
  265. return;
  266. }
  267. if (numWrite == 0) {
  268. log.info("Write " + numWrite + " ip: " + con.getIP());
  269. return;
  270. }
  271. /** not all data was send */
  272. if (wb.hasRemaining())
  273. return;
  274. }
  275. /**
  276. * Test if this build should use assertion. If NetworkAssertion == false javac will remove this code block
  277. */
  278. if (Assertion.NetworkAssertion) {
  279. assert !wb.hasRemaining();
  280. }
  281. /**
  282. * We wrote away all data, so we're no longer interested in writing on this socket.
  283. */
  284. key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
  285. /** We wrote all data so we can close connection that is "PandingClose" */
  286. if (con.isPendingClose())
  287. closeConnectionImpl(con);
  288. }
  289. /**
  290. * Connection will be closed [onlyClose()] and onDisconnect() method will be executed on another thread
  291. * [DisconnectionThreadPool] after getDisconnectionDelay() time in ms. This method may only be called by current
  292. * Dispatcher Thread.
  293. *
  294. * @param con
  295. */
  296. protected final void closeConnectionImpl(AConnection con) {
  297. /**
  298. * Test if this build should use assertion. If NetworkAssertion == false javac will remove this code block
  299. */
  300. if (Assertion.NetworkAssertion)
  301. assert Thread.currentThread() == this;
  302. if (con.onlyClose())
  303. dcPool.scheduleDisconnection(new DisconnectionTask(con), con.getDisconnectionDelay());
  304. }
  305. }