Java tutorial
/** * Copyright 2013 openteach * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.openteach.diamond.network.waverider.network; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.openteach.diamond.network.waverider.config.WaveriderConfig; import com.openteach.diamond.network.waverider.session.Session; import com.openteach.diamond.network.waverider.session.SessionManager; /** * <p> * TCP/IPMaster?, NIO * </p> * * @author <a href="mailto:sihai@taobao.com">sihai</a> * */ public class DefaultNetWorkServer implements NetWorkServer, Runnable { private static final Log logger = LogFactory.getLog(DefaultNetWorkServer.class); private String hostName; // ?? private int port; // ?? private ServerSocketChannel serverSocketChannel; // ?socket? private Selector selector; // ? private Thread netWorkServerThread; // ?, ? private SessionManager sessionManager; // ?? //private ExecutorService readerExecutor; // , ?, ??socket //private ExecutorService writerExecutor; // , ?, ??socket private AtomicBoolean isWeakuped; // selector private ConcurrentHashMap<SocketChannel, SocketChannelOPSChangeRequest> opsChangeRequstMap; // ? //============================================================= // //============================================================= public DefaultNetWorkServer() { this(null, WaveriderConfig.WAVERIDER_DEFAULT_PORT); } public DefaultNetWorkServer(int port) { this(null, port); } public DefaultNetWorkServer(String hostName, int port) { this.hostName = hostName; this.port = port; isWeakuped = new AtomicBoolean(false); } //============================================================= // LifeCycle //============================================================= @Override public boolean init() { try { selector = Selector.open(); serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); serverSocketChannel.socket() .bind(hostName == null ? new InetSocketAddress(port) : new InetSocketAddress(hostName, port)); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); // ?? // ?, socket? //readerExecutor = Executors.newFixedThreadPool(1, new WaveriderThreadFactory(NET_WORK_READER, null, true)); //writerExecutor = Executors.newFixedThreadPool(1, new WaveriderThreadFactory(NET_WORK_WRITER, null, true)); // ? netWorkServerThread = new Thread(this, NET_WORK_SERVER_THREAD_NAME); netWorkServerThread.setDaemon(true); } catch (IOException e) { logger.error("Init DefaultNetworkServer failed: ", e); throw new RuntimeException(e); } return true; } @Override public boolean start() { opsChangeRequstMap = new ConcurrentHashMap<SocketChannel, SocketChannelOPSChangeRequest>(); // ?? netWorkServerThread.start(); return true; } @Override public boolean stop() { // ??? //readerExecutor.shutdown(); //writerExecutor.shutdown(); // ?? netWorkServerThread.interrupt(); try { selector.close(); serverSocketChannel.close(); isWeakuped.set(false); opsChangeRequstMap.clear(); opsChangeRequstMap = null; } catch (IOException e) { logger.error("OOPSException", e); } return true; } @Override public boolean restart() { return stop() && init() && start(); } @Override public String getIp() { return serverSocketChannel.socket().getLocalSocketAddress().toString(); } @Override public int getPort() { return port; } @Override public boolean notifyWrite(SocketChannel channel) { logger.debug("notifyWrite"); if (channel == null) { return false; } SocketChannelOPSChangeRequest request = opsChangeRequstMap.get(channel); if (request == null) { // Socket return false; } // request.addOps(SelectionKey.OP_WRITE); // ?selector weakup(); return true; } @Override public boolean notifyRead(SocketChannel channel) { logger.debug("notifyRead"); if (channel == null) { return false; } SocketChannelOPSChangeRequest request = opsChangeRequstMap.get(channel); if (request == null) { // Socket return false; } // request.addOps(SelectionKey.OP_READ); // ?selector weakup(); return true; } @Override public void waitMoreData(long timeout) throws InterruptedException { throw new UnsupportedOperationException(); } private void dispatch() throws IOException { logger.debug("try dispatch"); SelectionKey key = null; for (SocketChannelOPSChangeRequest request : opsChangeRequstMap.values()) { key = request.getChannel().keyFor(selector); if (key != null) { // if ((request.getOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) { key.interestOps(SelectionKey.OP_WRITE); request.clearOps(SelectionKey.OP_WRITE); } else if ((request.getOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) { key.interestOps(SelectionKey.OP_READ); request.clearOps(SelectionKey.OP_READ); } } } isWeakuped.set(false); if (selector.select(WaveriderConfig.WAVERIDER_DEFAULT_NETWORK_TIME_OUT) <= 0) { return; } Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { key = (SelectionKey) iterator.next(); iterator.remove(); try { if (!key.isValid()) { continue; } else if (key.isAcceptable()) { onAccept(key); } else if (key.isReadable()) { //readerExecutor.execute(new NetworkTask(key, NETWORK_OPERATION_READ)); onRead(key); } else if (key.isWritable()) { //writerExecutor.execute(new NetworkTask(key, NETWORK_OPERATION_WRITE)); onWrite(key); } } catch (IOException e) { // opsChangeRequstMap.remove((SocketChannel) key.channel()); Session session = (Session) key.attachment(); if (session != null) { session.onException(e); // Session sessionManager.freeSession(session); } key.cancel(); key.channel().close(); e.printStackTrace(); logger.error("OOPSException", e); } } } @Override public void run() { logger.info(new StringBuilder("Waverider-NetWork-Server started listen on ") .append(hostName == null ? "" : hostName + ":").append(port).toString()); while (!Thread.currentThread().isInterrupted()) { try { dispatch(); } catch (IOException e) { logger.error("OOPSException", e); e.printStackTrace(); } } logger.info(new StringBuilder("Waverider-NetWork-Server stoped ").toString()); } /** * ?, ?Session, ?Session, * Session? * @param key * @throws IOException */ private void onAccept(SelectionKey key) throws IOException { SocketChannel channel = ((ServerSocketChannel) key.channel()).accept(); opsChangeRequstMap.put(channel, new SocketChannelOPSChangeRequest(channel, 0)); if (logger.isWarnEnabled()) logger.warn("Accept client from : " + channel.socket().getRemoteSocketAddress()); channel.configureBlocking(false); Session session = sessionManager.newSession(this, channel, false); channel.register(selector, SelectionKey.OP_READ, session); session.start(); } /** * ??, SessiononRead, ?Session? * @param key * @throws IOException * @throws InterruptedException */ private void onRead(SelectionKey key) throws IOException { Session session = (Session) key.attachment(); try { session.onRead(); } catch (InterruptedException e) { logger.error("OOPS Exception:", e); Thread.currentThread().interrupt(); } } /** * ??, SessiononWrite, Session?? * @param key * @throws IOException */ private void onWrite(SelectionKey key) throws IOException { key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); Session session = (Session) key.attachment(); session.onWrite(); //key.interestOps(SelectionKey.OP_READ); } /** * ?selector, ? */ private void weakup() { logger.debug("try to weakup"); if (isWeakuped.compareAndSet(false, true)) { this.selector.wakeup(); logger.debug("weakuped"); } } @Override public void setSessionManager(SessionManager sessionManager) { this.sessionManager = sessionManager; } /** * ?? * @author sihai * */ private class NetworkTask implements Runnable { private SelectionKey key; private int operation; public NetworkTask(SelectionKey key, int operation) { this.key = key; this.operation = operation; } @Override public void run() { try { switch (operation) { case NETWORK_OPERATION_ACCEPT: { onAccept(key); break; } case NETWORK_OPERATION_READ: { onRead(key); break; } case NETWORK_OPERATION_WRITE: { onWrite(key); break; } } } catch (IOException e) { logger.error("OOPSException", e); try { // Session session = (Session) key.attachment(); if (session != null) { session.onException(e); // Session sessionManager.freeSession(session); } opsChangeRequstMap.remove((SocketChannel) key.channel()); key.cancel(); key.channel().close(); } catch (IOException ex) { logger.error("OOPSException", ex); ex.printStackTrace(); } } } } }