PageRenderTime 5839ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/Server/tomcat/WEB-INF/src/com/drhelper/android/server/NoticeServer.java

https://github.com/weiganyi/dr-helper
Java | 591 lines | 447 code | 87 blank | 57 comment | 98 complexity | 659fe4ef30fa0522ae6f8908c7648a40 MD5 | raw file
  1. package com.drhelper.android.server;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.net.SocketAddress;
  5. import java.nio.ByteBuffer;
  6. import java.nio.channels.DatagramChannel;
  7. import java.nio.channels.SelectableChannel;
  8. import java.nio.channels.SelectionKey;
  9. import java.nio.channels.Selector;
  10. import java.nio.channels.ServerSocketChannel;
  11. import java.nio.channels.SocketChannel;
  12. import java.util.Iterator;
  13. import java.util.LinkedList;
  14. import java.util.Timer;
  15. import java.util.TimerTask;
  16. import com.alibaba.fastjson.JSON;
  17. import com.drhelper.android.bean.NoticeEvent;
  18. import com.drhelper.android.bean.UserSocketChannel;
  19. import com.drhelper.android.bean.com.NoticeHeartBeat;
  20. import com.drhelper.android.bean.com.NoticeLogin;
  21. import com.drhelper.android.bean.com.NoticeLogout;
  22. import com.drhelper.android.bean.com.NoticePush;
  23. import com.drhelper.android.bean.com.NoticeSubscribe;
  24. import com.drhelper.android.util.LogicException;
  25. import com.drhelper.android.util.TypeConvert;
  26. import com.drhelper.common.db.DBManager;
  27. import com.drhelper.common.entity.Order;
  28. import com.drhelper.common.entity.User;
  29. public class NoticeServer implements Runnable {
  30. public static final int listenPort = 30000;
  31. private Selector selector;
  32. private LinkedList<UserSocketChannel> loginChanList;
  33. private LinkedList<UserSocketChannel> emptyTableChanList;
  34. private LinkedList<UserSocketChannel> finishMenuChanList;
  35. private LinkedList<UserSocketChannel> waitHeartBeatChanList;
  36. private Timer timer;
  37. public static final String heartBeatEvent = "heartbeat";
  38. public final String emptyTableEvent = "emptytable";
  39. public final String finishMenuEvent = "finishmenu";
  40. private DatagramChannel eventChannel = null;
  41. public NoticeServer() {
  42. loginChanList = new LinkedList<UserSocketChannel>();
  43. emptyTableChanList = new LinkedList<UserSocketChannel>();
  44. finishMenuChanList = new LinkedList<UserSocketChannel>();
  45. waitHeartBeatChanList = new LinkedList<UserSocketChannel>();
  46. }
  47. @Override
  48. public void run() {
  49. System.out.println("NoticeServer.run(): thread enter");
  50. try {
  51. // do some prepare work
  52. initServer();
  53. } catch (IOException e) {
  54. System.out.println("NoticeServer.run(): initServer catch Exception: " + e.getMessage());
  55. return;
  56. }
  57. //check if thread had been interrupted
  58. while (!Thread.interrupted()) {
  59. try {
  60. // select any event
  61. selector.select();
  62. // iterate to process the event
  63. Iterator<SelectionKey> it = selector.selectedKeys().iterator();
  64. while (it.hasNext()) {
  65. // get the event
  66. SelectionKey key = it.next();
  67. // client connect event
  68. if (key.isValid() && key.isAcceptable()) {
  69. // accept a connect
  70. ServerSocketChannel srvChan = (ServerSocketChannel) key.channel();
  71. SocketChannel channel = srvChan.accept();
  72. channel.configureBlocking(false);
  73. channel.register(selector, SelectionKey.OP_READ);
  74. // client message event
  75. } else if (key.isValid() && key.isReadable()) {
  76. SelectableChannel channel = key.channel();
  77. // channel to the client
  78. if (channel instanceof SocketChannel) {
  79. SocketChannel socketChannel = (SocketChannel) channel;
  80. // read the message from client
  81. ByteBuffer rBuf = ByteBuffer.allocate(256);
  82. socketChannel.read(rBuf);
  83. String buffer = TypeConvert.getStringFromByteBuffer(rBuf);
  84. if (buffer != null && buffer.equals("") != true) {
  85. // check if is heartbeat request
  86. NoticeHeartBeat hbResp = JSON.parseObject(buffer, NoticeHeartBeat.class);
  87. if (hbResp != null &&
  88. hbResp.getMsg() != null &&
  89. hbResp.getMsg().equals(heartBeatEvent) == true) {
  90. doHeartBeatReq(hbResp, socketChannel);
  91. continue;
  92. }
  93. // check if is login request
  94. NoticeLogin loginReq = JSON.parseObject(buffer, NoticeLogin.class);
  95. if (loginReq != null
  96. && loginReq.getLoginUserName() != null
  97. && loginReq.getLoginUserPasswd() != null) {
  98. doLogin(loginReq, socketChannel, key);
  99. continue;
  100. }
  101. // check if is logout request
  102. NoticeLogout logoutReq = JSON.parseObject(buffer, NoticeLogout.class);
  103. if (logoutReq != null
  104. && logoutReq.getLogoutUserName() != null) {
  105. doLogout(logoutReq, socketChannel);
  106. continue;
  107. }
  108. // check if is subscribe request
  109. NoticeSubscribe subReq = JSON.parseObject(buffer, NoticeSubscribe.class);
  110. if (subReq != null) {
  111. doSubscribe(subReq, socketChannel, key);
  112. continue;
  113. }
  114. }
  115. // channel to the heartbeat
  116. } else if (channel instanceof DatagramChannel) {
  117. DatagramChannel datagramChannel = (DatagramChannel) channel;
  118. // receive the event
  119. ByteBuffer rBuf = ByteBuffer.allocate(256);
  120. datagramChannel.receive(rBuf);
  121. String buffer = TypeConvert.getStringFromByteBuffer(rBuf);
  122. if (buffer != null && buffer.equals("") != true) {
  123. NoticeEvent eventReq = JSON.parseObject(buffer, NoticeEvent.class);
  124. // check if is heartbeat event
  125. if (eventReq.getEvent().equals(heartBeatEvent) == true) {
  126. doHeartBeatEvent();
  127. continue;
  128. }
  129. // check if is emptytable event
  130. if (eventReq.getEvent().equals(emptyTableEvent) == true) {
  131. doEmptyTableEvent(eventReq.getUserName());
  132. continue;
  133. }
  134. // check if is finishmenu event
  135. if (eventReq.getEvent().equals(finishMenuEvent) == true) {
  136. doFinishMenuEvent(eventReq.getUserName());
  137. continue;
  138. }
  139. }
  140. }
  141. }
  142. // delete the finished event
  143. it.remove();
  144. }
  145. } catch (Exception e) {
  146. System.out.println("NoticeServer.run(): while catch Exception: " + e.getMessage());
  147. }
  148. }
  149. System.out.println("NoticeServer.run(): thread exit");
  150. return;
  151. }
  152. private void initServer() throws IOException {
  153. //create a tcp server channel for client
  154. ServerSocketChannel srvChan = ServerSocketChannel.open();
  155. srvChan.configureBlocking(false);
  156. srvChan.socket().bind(new InetSocketAddress(listenPort));
  157. //create a selector
  158. selector = Selector.open();
  159. //register the tcp socket channel into the selector
  160. srvChan.register(selector, SelectionKey.OP_ACCEPT);
  161. //create a udp server channel for event message
  162. DatagramChannel dataChan = DatagramChannel.open();
  163. dataChan.configureBlocking(false);
  164. dataChan.socket().bind(new InetSocketAddress(listenPort));
  165. //register the udp socket channel into the selector
  166. dataChan.register(selector, SelectionKey.OP_READ);
  167. //create a udp client channel for event message
  168. eventChannel = DatagramChannel.open();
  169. InetSocketAddress address = new InetSocketAddress(listenPort);
  170. //bind the local port into the channel
  171. eventChannel.connect(address);
  172. //start a timer to do heartbeat
  173. timer = new Timer();
  174. HeartBeatTask task = new HeartBeatTask();
  175. timer.schedule(task, 5000, 30000); //after 5s to start, loop 30s timeout
  176. }
  177. private void doLogin(NoticeLogin loginReq, SocketChannel channel, SelectionKey key) {
  178. String userName = loginReq.getLoginUserName();
  179. String userPasswd = loginReq.getLoginUserPasswd();
  180. boolean result = true;
  181. try {
  182. if (userName == null || userPasswd == null
  183. || userName.length() == 0 || userPasswd.length() == 0) {
  184. System.out.println("NoticeServer.doLogin(): userName or userPasswd is null");
  185. throw new LogicException();
  186. }
  187. // check the user and passwd
  188. DBManager db = new DBManager();
  189. User user = db.getUser(userName, userPasswd);
  190. if (user != null) {
  191. // add the login channel into the loginChanList
  192. if (findChannel(loginChanList, channel) == null) {
  193. addChannel(loginChanList, userName, channel, key);
  194. }
  195. }
  196. } catch (LogicException e) {
  197. System.out.println("NoticeServer.doLogin(): catch LogicException: " + e.getMessage());
  198. result = false;
  199. } finally {
  200. //create the response
  201. NoticeLogin loginResp = new NoticeLogin();
  202. loginResp.setLoginUserName(loginReq.getLoginUserName());
  203. loginResp.setLoginUserPasswd(loginReq.getLoginUserPasswd());
  204. loginResp.setResult(result);
  205. //send the response
  206. String response = JSON.toJSONString(loginResp);
  207. ByteBuffer sBuf = TypeConvert.getByteBufferFromString(response);
  208. try {
  209. channel.write(sBuf);
  210. } catch (IOException e) {
  211. System.out.println("NoticeServer.doLogin(): write channel catch IOException: " + e.getMessage());
  212. }
  213. }
  214. }
  215. private void doLogout(NoticeLogout logoutReq, SocketChannel channel) throws IOException {
  216. String userName = logoutReq.getLogoutUserName();
  217. boolean result = true;
  218. try {
  219. if (userName == null || userName.length() == 0) {
  220. System.out.println("NoticeServer.doLogout(): userName is null");
  221. throw new LogicException();
  222. }
  223. //remove channel if exist
  224. UserSocketChannel item = findChannel(loginChanList, channel);
  225. if (item != null) {
  226. item.getKey().cancel();
  227. removeChannel(loginChanList, channel);
  228. removeChannel(emptyTableChanList, channel);
  229. removeChannel(finishMenuChanList, channel);
  230. removeChannel(waitHeartBeatChanList, channel);
  231. }
  232. }catch (LogicException e) {
  233. System.out.println("NoticeServer.doLogout(): catch LogicException: " + e.getMessage());
  234. result = false;
  235. }finally {
  236. //create the response
  237. NoticeLogout logoutResp = new NoticeLogout();
  238. logoutResp.setLogoutUserName(logoutReq.getLogoutUserName());
  239. logoutResp.setResult(result);
  240. //send the response
  241. String response = JSON.toJSONString(logoutResp);
  242. ByteBuffer sBuf = TypeConvert.getByteBufferFromString(response);
  243. try {
  244. channel.write(sBuf);
  245. } catch (IOException e) {
  246. System.out.println("NoticeServer.doLogout(): write channel catch IOException: " + e.getMessage());
  247. }
  248. //close the socket
  249. channel.close();
  250. }
  251. }
  252. private void doSubscribe(NoticeSubscribe subReq, SocketChannel channel, SelectionKey key) {
  253. boolean isEmptyTable = subReq.isEmptyTable();
  254. boolean isFinishMenu = subReq.isFinishMenu();
  255. boolean result = true;
  256. try {
  257. // check the client if had already logined
  258. UserSocketChannel lgChan = findChannel(loginChanList, channel);
  259. if (lgChan != null) {
  260. String userName = lgChan.getUserName();
  261. //update the empty table list
  262. UserSocketChannel etChan = findChannel(emptyTableChanList, channel);
  263. if (isEmptyTable && etChan == null) {
  264. addChannel(emptyTableChanList, userName, channel, key);
  265. }else if (!isEmptyTable && etChan != null) {
  266. removeChannel(emptyTableChanList, channel);
  267. }
  268. //update the finish menu list
  269. UserSocketChannel fmChan = findChannel(finishMenuChanList, channel);
  270. if (isFinishMenu && fmChan == null) {
  271. addChannel(finishMenuChanList, userName, channel, key);
  272. }else if (!isFinishMenu && fmChan != null) {
  273. removeChannel(finishMenuChanList, channel);
  274. }
  275. }else {
  276. throw new LogicException();
  277. }
  278. }catch (LogicException e) {
  279. System.out.println("NoticeServer.doSubscribe(): catch LogicException: " + e.getMessage());
  280. result = false;
  281. }finally {
  282. //create the response
  283. NoticeSubscribe subResp = new NoticeSubscribe();
  284. subResp.setEmptyTable(isEmptyTable);
  285. subResp.setFinishMenu(isFinishMenu);
  286. subResp.setResult(result);
  287. //send the response
  288. String response = JSON.toJSONString(subResp);
  289. ByteBuffer sBuf = TypeConvert.getByteBufferFromString(response);
  290. try {
  291. channel.write(sBuf);
  292. } catch (IOException e) {
  293. System.out.println("NoticeServer.doSubscribe(): write channel catch IOException: " + e.getMessage());
  294. }
  295. }
  296. }
  297. private void doHeartBeatReq(NoticeHeartBeat hbResp, SocketChannel channel) {
  298. //create NoticeHeartBeat response object
  299. try {
  300. NoticeHeartBeat nhbResp = new NoticeHeartBeat();
  301. nhbResp.setMsg(heartBeatEvent);
  302. nhbResp.setResult(true);
  303. //serialize by fastjson
  304. String response = JSON.toJSONString(nhbResp);
  305. ByteBuffer sBuf = TypeConvert.getByteBufferFromString(response);
  306. //send the heartbeat response
  307. channel.write(sBuf);
  308. } catch (IOException e) {
  309. System.out.println("NoticeServer.doHeartBeatReq(): catch IOException: " + e.getMessage());
  310. }
  311. //remove it from waitHeartBeatChanList
  312. removeChannel(waitHeartBeatChanList, channel);
  313. }
  314. private void doHeartBeatEvent() {
  315. UserSocketChannel prevItem = null;
  316. SocketChannel channel = null;
  317. //close all connect in waitHeartBeatChanList, because it means the remote connect had lost.
  318. for (UserSocketChannel item : waitHeartBeatChanList) {
  319. //first to process the prev item
  320. if (prevItem != null) {
  321. channel = prevItem.getChannel();
  322. prevItem.getKey().cancel();
  323. removeChannel(waitHeartBeatChanList, channel);
  324. removeChannel(loginChanList, channel);
  325. removeChannel(emptyTableChanList, channel);
  326. removeChannel(finishMenuChanList, channel);
  327. try {
  328. channel.close();
  329. } catch (IOException e) {
  330. System.out.println("NoticeServer.doHeartBeatEvent(): channel.close catch IOException: " + e.getMessage());
  331. }
  332. prevItem = null;
  333. }
  334. //heartbeat not response , think this connect had dead
  335. prevItem = item;
  336. }
  337. //then to process the leave item
  338. if (prevItem != null) {
  339. channel = prevItem.getChannel();
  340. prevItem.getKey().cancel();
  341. removeChannel(waitHeartBeatChanList, channel);
  342. removeChannel(loginChanList, channel);
  343. removeChannel(emptyTableChanList, channel);
  344. removeChannel(finishMenuChanList, channel);
  345. try {
  346. channel.close();
  347. } catch (IOException e) {
  348. System.out.println("NoticeServer.doHeartBeatEvent(): channel.close catch IOException: " + e.getMessage());
  349. }
  350. prevItem = null;
  351. }
  352. //move all channels into waitHeartBeatChanList
  353. for (UserSocketChannel item : loginChanList) {
  354. addChannel(waitHeartBeatChanList, item);
  355. }
  356. }
  357. private void doEmptyTableEvent(String userName) {
  358. for (UserSocketChannel item : emptyTableChanList) {
  359. SocketChannel channel = item.getChannel();
  360. NoticePush hpReq = new NoticePush();
  361. hpReq.setEvent(emptyTableEvent);
  362. String request = JSON.toJSONString(hpReq);
  363. ByteBuffer sBuf = TypeConvert.getByteBufferFromString(request);
  364. //send the emptytable request
  365. try {
  366. channel.write(sBuf);
  367. } catch (IOException e) {
  368. System.out.println("NoticeServer.doEmptyTableEvent(): channel.write catch IOException: " + e.getMessage());
  369. }
  370. }
  371. }
  372. private void doFinishMenuEvent(String userName) {
  373. for (UserSocketChannel item : finishMenuChanList) {
  374. String user = item.getUserName();
  375. if (user.equals(userName) == true) {
  376. SocketChannel channel = item.getChannel();
  377. NoticePush hpReq = new NoticePush();
  378. hpReq.setEvent(finishMenuEvent);
  379. String request = JSON.toJSONString(hpReq);
  380. ByteBuffer sBuf = TypeConvert.getByteBufferFromString(request);
  381. //send the finishmenu request
  382. try {
  383. channel.write(sBuf);
  384. return;
  385. } catch (IOException e) {
  386. System.out.println("NoticeServer.doFinishMenuEvent(): channel.write catch IOException: " + e.getMessage());
  387. }
  388. }
  389. }
  390. }
  391. public void publishEmptyTableNotice() {
  392. publishEvent(emptyTableEvent, "");
  393. }
  394. public void publishFinishMenuNotice(int orderNum) {
  395. //get the user of this order
  396. DBManager db = new DBManager();
  397. Order orderObj = db.getOrderObjByOrder(orderNum);
  398. String waiter = orderObj.getWaiter();
  399. if (waiter != null && waiter.equals("") != true) {
  400. publishEvent(finishMenuEvent, waiter);
  401. }
  402. }
  403. private synchronized void publishEvent(String event, String userName) {
  404. if (eventChannel != null) {
  405. NoticeEvent obj = new NoticeEvent();
  406. obj.setEvent(event);
  407. obj.setUserName(userName);
  408. String buffer = JSON.toJSONString(obj);
  409. ByteBuffer sBuf = TypeConvert.getByteBufferFromString(buffer);
  410. //send the event to server
  411. try {
  412. InetSocketAddress address = new InetSocketAddress(NoticeServer.listenPort);
  413. eventChannel.send(sBuf, address);
  414. } catch (IOException e) {
  415. System.out.println("NoticeServer.publishEvent(): channel.send catch IOException: " + e.getMessage());
  416. }
  417. }
  418. }
  419. private UserSocketChannel findChannel(LinkedList<UserSocketChannel> list,
  420. SocketChannel channel) {
  421. SocketChannel chan = null;
  422. UserSocketChannel findItem = null;
  423. for (UserSocketChannel item : list) {
  424. chan = item.getChannel();
  425. if (chan.equals(channel) == true) {
  426. findItem = item;
  427. break;
  428. }
  429. }
  430. return findItem;
  431. }
  432. private UserSocketChannel addChannel(LinkedList<UserSocketChannel> list,
  433. String userName,
  434. SocketChannel channel,
  435. SelectionKey key) {
  436. UserSocketChannel item = new UserSocketChannel();
  437. item.setChannel(channel);
  438. item.setUserName(userName);
  439. item.setKey(key);
  440. list.add(item);
  441. return item;
  442. }
  443. private UserSocketChannel addChannel(LinkedList<UserSocketChannel> list,
  444. UserSocketChannel item) {
  445. list.add(item);
  446. return item;
  447. }
  448. public boolean removeChannel(LinkedList<UserSocketChannel> list,
  449. SocketChannel channel) {
  450. SocketChannel chan = null;
  451. UserSocketChannel findItem = null;
  452. for (UserSocketChannel item : list) {
  453. chan = item.getChannel();
  454. if (chan.equals(channel) == true) {
  455. findItem = item;
  456. break;
  457. }
  458. }
  459. if (findItem != null) {
  460. list.remove(findItem);
  461. return true;
  462. }else {
  463. return false;
  464. }
  465. }
  466. }
  467. class HeartBeatTask extends TimerTask {
  468. private DatagramChannel channel = null;
  469. private SocketAddress address;
  470. public HeartBeatTask() {
  471. //create a udp channel
  472. try {
  473. channel = DatagramChannel.open();
  474. } catch (IOException e) {
  475. channel = null;
  476. System.out.println("HeartBeatTask.HeartBeatTask(): DatagramChannel.open catch IOException: " + e.getMessage());
  477. return;
  478. }
  479. address = new InetSocketAddress(NoticeServer.listenPort);
  480. //bind the local port into the channel
  481. try {
  482. channel.connect(address);
  483. } catch (IOException e) {
  484. channel = null;
  485. System.out.println("HeartBeatTask.HeartBeatTask(): channel.connect catch IOException: " + e.getMessage());
  486. return;
  487. }
  488. }
  489. @Override
  490. public void run() {
  491. if (channel != null) {
  492. NoticeEvent obj = new NoticeEvent();
  493. obj.setEvent(NoticeServer.heartBeatEvent);
  494. String buffer = JSON.toJSONString(obj);
  495. ByteBuffer sBuf = TypeConvert.getByteBufferFromString(buffer);
  496. //send the heartbeat event to server
  497. try {
  498. channel.send(sBuf, address);
  499. } catch (IOException e) {
  500. System.out.println("HeartBeatTask.run(): channel.send catch IOException: " + e.getMessage());
  501. }
  502. }
  503. }
  504. }