PageRenderTime 57ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/Commons/src/com/aionengine/commons/network/Reader.java

http://aionxemu.googlecode.com/
Java | 473 lines | 383 code | 71 blank | 19 comment | 97 complexity | 34f10c1b1d6ed1d32ada7e24ac089994 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.aionengine.commons.network;
  18. import com.aionemu.commons.utils.Rnd;
  19. import java.io.IOException;
  20. import java.nio.channels.*;
  21. import java.util.ArrayList;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.Set;
  25. /**
  26. * @author xavier
  27. */
  28. public class Reader extends Processor {
  29. private Selector selector, retrySelector;
  30. private List<Worker> workers;
  31. private List<SelectionKey> keys;
  32. private boolean workersEnabled;
  33. private int workerThreads;
  34. private int bufferCount;
  35. private boolean waitingFreeWorker = false;
  36. private Worker elected = null;
  37. private int minQueueSize = Integer.MAX_VALUE;
  38. private boolean identics = true;
  39. private int last = 0;
  40. private int queueSize = 0;
  41. private int nbFull = 0;
  42. private List<Worker> idles = new ArrayList<Worker>();
  43. private int i = 0;
  44. private Worker w;
  45. private SocketChannel sc = null;
  46. private int read = 0, bufferSize = 0, oldLimit = 0, totalRead = 0, maxTries = 3, tries = 0, retrySelection;
  47. private long timeLostInRetries = 0, before = 0;
  48. private Object gate;
  49. private boolean debugEnabled = false;
  50. public Reader(String name, boolean workersEnabled, int workerThreads, int bufferCount, int maxTries, boolean debugEnabled) throws IOException {
  51. super(name, workersEnabled);
  52. this.keys = new ArrayList<SelectionKey>();
  53. this.selector = Selector.open();
  54. this.retrySelector = Selector.open();
  55. this.workersEnabled = workersEnabled;
  56. this.workerThreads = workerThreads;
  57. this.bufferCount = bufferCount;
  58. this.gate = new Object();
  59. this.maxTries = maxTries;
  60. this.debugEnabled = debugEnabled;
  61. if (workersEnabled)
  62. initWorkers();
  63. }
  64. private void initWorkers() {
  65. workers = new ArrayList<Worker>();
  66. for (int i = 0; i < workerThreads; i++) {
  67. Worker worker = new Worker(getName() + "-worker-" + (i + 1), this, bufferCount);
  68. workers.add(worker);
  69. worker.start();
  70. }
  71. }
  72. public boolean isWaitingFreeWorker() {
  73. return waitingFreeWorker;
  74. }
  75. private Worker choose() {
  76. idles.clear();
  77. nbFull = 0;
  78. last = 0;
  79. identics = true;
  80. elected = null;
  81. minQueueSize = Integer.MAX_VALUE;
  82. for (i = 0; i < workers.size(); i++) {
  83. w = workers.get(i);
  84. if (!w.isIdle()) {
  85. if (w.isFull()) {
  86. nbFull++;
  87. continue;
  88. }
  89. queueSize = w.getQueueSize();
  90. if (last == 0) {
  91. last = queueSize;
  92. } else {
  93. if (last != queueSize) {
  94. identics = false;
  95. }
  96. }
  97. if (queueSize < minQueueSize) {
  98. minQueueSize = queueSize;
  99. elected = w;
  100. }
  101. } else {
  102. idles.add(w);
  103. }
  104. }
  105. if (idles.size() > 0) {
  106. elected = idles.get(Rnd.get(idles.size()));
  107. } else {
  108. if (nbFull == workers.size() || identics) {
  109. elected = workers.get(Rnd.get(workers.size()));
  110. }
  111. }
  112. return elected;
  113. }
  114. public void wakeup() {
  115. synchronized (gate) {
  116. gate.notify();
  117. }
  118. }
  119. @Override
  120. public void manage(Connection conn) throws RuntimeException {
  121. if (!conn.channel().isOpen()) {
  122. throw new RuntimeException("Trying to manage a connection while channel is already closed");
  123. }
  124. synchronized (gate) {
  125. selector.wakeup();
  126. if (conn.channel().isOpen()) {
  127. SelectionKey key;
  128. try {
  129. key = conn.channel().register(selector, SelectionKey.OP_READ, conn);
  130. }
  131. catch (ClosedChannelException e) {
  132. throw new RuntimeException("Trying to manage a connection while channel is already closed", e);
  133. }
  134. keys.add(key);
  135. }
  136. }
  137. }
  138. @Override
  139. public void close() {
  140. super.close();
  141. synchronized (gate) {
  142. selector.wakeup();
  143. if (workersEnabled) {
  144. for (Worker w : workers) {
  145. w.end();
  146. try {
  147. w.join();
  148. } catch (InterruptedException e) {
  149. }
  150. }
  151. }
  152. try {
  153. for (SelectionKey key : keys) {
  154. if (key.isValid()) {
  155. Connection c = (Connection) key.attachment();
  156. key.attach(null);
  157. key.cancel();
  158. if (c.channel().keyFor(retrySelector) != null) {
  159. c.channel().keyFor(retrySelector).cancel();
  160. }
  161. c.close(true);
  162. }
  163. }
  164. } catch (CancelledKeyException e) {
  165. }
  166. keys.clear();
  167. }
  168. }
  169. @Override
  170. public int getNumberOfConnections() {
  171. return keys.size();
  172. }
  173. @Override
  174. public void run() {
  175. int selection = 0;
  176. Iterator<SelectionKey> ski;
  177. SelectionKey sk;
  178. Set<SelectionKey> sks;
  179. Connection c;
  180. imRunning();
  181. while (running()) {
  182. try {
  183. imIdle();
  184. selection = selector.select();
  185. imBusy();
  186. if (selection > 0) {
  187. sks = selector.selectedKeys();
  188. for (ski = sks.iterator(); ski.hasNext();) {
  189. sk = ski.next();
  190. ski.remove();
  191. c = (Connection) sk.attachment();
  192. if (c == null)
  193. continue;
  194. if (!sk.isValid()) {
  195. if (keys.contains(sk)) {
  196. keys.remove(sk);
  197. }
  198. c.close(false);
  199. continue;
  200. }
  201. if (sk.isValid() && sk.isReadable()) {
  202. read(c);
  203. } else {
  204. c.close(false);
  205. }
  206. }
  207. }
  208. synchronized (gate) {
  209. }
  210. }
  211. catch (Exception e) {
  212. if (debugEnabled)
  213. log.error(e.getClass().getSimpleName() + " while processing connection", e);
  214. }
  215. }
  216. try {
  217. selector.close();
  218. retrySelector.close();
  219. } catch (IOException e) {
  220. log.error("Exception while closing selector", e);
  221. }
  222. if (debugEnabled)
  223. log.debug(getName() + " stopped");
  224. }
  225. private void read(Connection c) throws IOException {
  226. switch (c.getMode()) {
  227. case BINARY:
  228. readBinary(c);
  229. break;
  230. case TEXT:
  231. readText(c);
  232. break;
  233. }
  234. }
  235. private void readText(Connection c) throws IOException {
  236. sc = c.channel();
  237. buffer.clear();
  238. if (sc == null || c.isWriteDisabled() || !sc.isOpen()) {
  239. c.close(false);
  240. }
  241. try {
  242. synchronized (sc) {
  243. read = sc.read(buffer);
  244. }
  245. }
  246. catch (IOException e) {
  247. if (debugEnabled)
  248. log.debug("IOException reading from connection " + c, e);
  249. c.close(false);
  250. }
  251. if (read > 0) {
  252. buffer.flip();
  253. if (debugEnabled)
  254. log.debug("Connection " + c + " about to process data from buffer " + buffer);
  255. if (workersEnabled) {
  256. try {
  257. choose().add(c, buffer);
  258. } catch (RuntimeException e) {
  259. if (debugEnabled)
  260. log.debug("Cannot queue packet " + buffer + " for connection " + c, e);
  261. c.close(false);
  262. }
  263. } else {
  264. try {
  265. if (!c.processData(buffer)) {
  266. c.close(false);
  267. return;
  268. }
  269. } catch (Exception e) {
  270. log.error(e.getClass().getSimpleName() + " while processing buffer " + buffer + " for connection " + c, e);
  271. }
  272. }
  273. } else if (read < 0) {
  274. c.close(false);
  275. }
  276. }
  277. private void readBinary(Connection c) throws IOException {
  278. sc = c.channel();
  279. buffer.clear();
  280. oldLimit = 0;
  281. bufferSize = 0;
  282. read = 0;
  283. totalRead = 0;
  284. if (sc == null || c.isWriteDisabled() || !sc.isOpen()) {
  285. c.close(false);
  286. }
  287. try {
  288. synchronized (sc) {
  289. totalRead = read = sc.read(buffer);
  290. }
  291. }
  292. catch (IOException e) {
  293. if (debugEnabled)
  294. log.debug("IOException reading from connection " + c, e);
  295. c.close(false);
  296. }
  297. if (read > 0) {
  298. bufferSize = buffer.getShort(0);
  299. tries = 0;
  300. if (debugEnabled)
  301. log.debug("Connection " + c + " about to process " + buffer + ", packet size: " + bufferSize);
  302. while (buffer.position() < bufferSize && tries < maxTries) {
  303. tries++;
  304. before = System.currentTimeMillis();
  305. synchronized (sc) {
  306. totalRead += read = sc.read(buffer);
  307. }
  308. if (read == 0) {
  309. if (sc.keyFor(retrySelector) == null) {
  310. sc.register(retrySelector, SelectionKey.OP_READ);
  311. } else {
  312. sc.keyFor(retrySelector).interestOps(SelectionKey.OP_READ);
  313. }
  314. retrySelection = retrySelector.select();
  315. if (retrySelection > 0) {
  316. retrySelector.selectedKeys().clear();
  317. }
  318. }
  319. }
  320. if (sc.keyFor(retrySelector) != null) {
  321. sc.keyFor(retrySelector).interestOps(0);
  322. }
  323. if (tries == maxTries) {
  324. if (debugEnabled)
  325. log.error("Too much read tries (" + maxTries + ") without any bytes read (read: " + totalRead + ", remaining: " + buffer.remaining() + ") for connection " + c + ", kicking client");
  326. c.close(false);
  327. return;
  328. } else if (tries > 0 && debugEnabled) {
  329. timeLostInRetries += System.currentTimeMillis() - before;
  330. log.debug("Read successfully " + totalRead + " bytes after " + tries + " tries (total time lost: " + timeLostInRetries + " ms)");
  331. }
  332. buffer.flip();
  333. while (buffer.remaining() > 2 && buffer.remaining() >= buffer.getShort(buffer.position())) {
  334. try {
  335. bufferSize = buffer.getShort();
  336. if (bufferSize > 1) {
  337. bufferSize -= 2;
  338. }
  339. oldLimit = buffer.limit() - 2;
  340. buffer.compact();
  341. buffer.limit(bufferSize);
  342. buffer.position(0);
  343. } catch (IllegalArgumentException e) {
  344. if (debugEnabled)
  345. log.debug("Illegal argument while parsing buffer " + buffer + " (read: " + totalRead + ", awaited: " + bufferSize + ") for connection " + c, e);
  346. c.close(false);
  347. return;
  348. }
  349. if (debugEnabled)
  350. log.debug("Connection " + c + " about to process data from buffer " + buffer);
  351. if (workersEnabled) {
  352. try {
  353. choose().add(c, buffer);
  354. } catch (RuntimeException e) {
  355. if (debugEnabled)
  356. log.debug("Cannot queue packet " + buffer + " for connection " + c, e);
  357. c.close(false);
  358. }
  359. } else {
  360. try {
  361. if (c.processData(buffer)) {
  362. if (buffer.position() != bufferSize) {
  363. if (debugEnabled)
  364. log.debug("After processing, buffer position is not as expected: expected " + (bufferSize) + ", buffer: " + buffer + ", fixing...");
  365. buffer.position(bufferSize);
  366. }
  367. } else {
  368. c.close(false);
  369. return;
  370. }
  371. } catch (Exception e) {
  372. if (debugEnabled)
  373. log.error(e.getClass().getSimpleName() + " while processing buffer " + buffer + " for connection " + c, e);
  374. c.close(false);
  375. }
  376. }
  377. if (oldLimit > buffer.position()) {
  378. buffer.limit(oldLimit);
  379. if (debugEnabled)
  380. log.debug("Connection " + c + ": buffer " + buffer + " has more packets (old limit: " + oldLimit + ", last packet size: " + bufferSize + ", next packet size: " + buffer.getShort(buffer.position()) + ") ...");
  381. buffer.compact();
  382. buffer.position(0);
  383. buffer.limit(oldLimit - bufferSize);
  384. if (debugEnabled)
  385. log.debug("Connection " + c + " about to process next packet from buffer " + buffer + ", next packet size: " + buffer.getShort(0));
  386. } else {
  387. if (debugEnabled)
  388. log.debug("Connection " + c + " buffer " + buffer + " seems entirely read, old limit: " + oldLimit + ", read: " + totalRead);
  389. break;
  390. }
  391. }
  392. if (buffer.hasRemaining()) {
  393. if (debugEnabled)
  394. log.error("Buffer " + buffer + " still has data (awaited: " + bufferSize + ", read: " + totalRead + "), discarding and closing connection " + c + "...");
  395. c.close(false);
  396. return;
  397. }
  398. } else if (read < 0) {
  399. c.close(false);
  400. }
  401. }
  402. }