Java tutorial
/* * LumaQQ - Java QQ Client * * Copyright (C) 2004 notXX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package edu.tsinghua.lumaqq.qq.net; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.DatagramChannel; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import edu.tsinghua.lumaqq.qq.packets.PacketParseException; /** * ?? * * @author notxx */ public final class Porter extends Thread { /** Logger */ private static final Log log = LogFactory.getLog(Porter.class); /** ?? */ protected boolean shutdown = false; /** ? */ protected Selector selector; // port private List<IPort> ports; // proxy private List<IProxy> proxies; // private Queue<Object> disposeQueue; // private List<Object> newConnections; /** * Porter. */ public Porter() { ports = new ArrayList<IPort>(); proxies = new ArrayList<IProxy>(); newConnections = new Vector<Object>(); disposeQueue = new LinkedList<Object>(); setName("Porter"); setDaemon(true); // Selector try { selector = Selector.open(); } catch (IOException e) { log.debug(e); throw new RuntimeException(e); } } /** * portporter * * @param port * IPort * @throws ClosedChannelException * */ public void register(IPort port) throws ClosedChannelException { SelectableChannel channel = port.channel(); if (channel instanceof SocketChannel) channel.register(selector, SelectionKey.OP_CONNECT, port.getNIOHandler()); else if (channel instanceof DatagramChannel) channel.register(selector, SelectionKey.OP_READ, port.getNIOHandler()); if (!ports.contains(port)) ports.add(port); } /** * ?channel * * @param port * @param ops * @throws ClosedChannelException */ public void register(IPort port, int ops) throws ClosedChannelException { SelectableChannel channel = port.channel(); if (channel instanceof SocketChannel) channel.register(selector, ops, port.getNIOHandler()); else if (channel instanceof DatagramChannel) channel.register(selector, ops, port.getNIOHandler()); if (!ports.contains(port)) ports.add(port); } /** * ??? * * @param proxy * IProxy * @throws ClosedChannelException * */ public void register(IProxy proxy) throws ClosedChannelException { SelectableChannel channel = proxy.channel(); if (channel instanceof SocketChannel) channel.register(selector, SelectionKey.OP_CONNECT, proxy.getNIOHandler()); else if (channel instanceof DatagramChannel) channel.register(selector, SelectionKey.OP_READ, proxy.getNIOHandler()); if (!proxies.contains(proxy)) proxies.add(proxy); } /** * portportchannel * * @param port * IPort * @throws IOException */ private void deregister(IPort port) { if (port == null) return; if (!ports.remove(port)) return; SelectionKey key = port.channel().keyFor(selector); if (key != null) key.cancel(); port.dispose(); } /** * proxy * * @param proxy */ private void deregister(IProxy proxy) { if (proxy == null) return; if (!proxies.remove(proxy)) return; SelectionKey key = proxy.channel().keyFor(selector); if (key != null) key.cancel(); proxy.dispose(); } /** * ??port * * @param e * ??Exception */ private void dispatchErrorToAll(Exception e) { for (IPort port : ports) port.getNIOHandler().processError(e); for (IProxy proxy : proxies) proxy.getNIOHandler().processError(e); } /** * port?? * @throws IOException */ private void notifySend() { int size = ports.size(); for (int i = 0; i < size; i++) { INIOHandler handler = null; try { handler = (ports.get(i)).getNIOHandler(); handler.processWrite(); } catch (IOException e) { log.error(e.getMessage()); handler.processError(e); } catch (IndexOutOfBoundsException e) { } } size = proxies.size(); for (int i = 0; i < size; i++) { INIOHandler handler = null; try { handler = (proxies.get(i)).getNIOHandler(); handler.processWrite(); } catch (IOException e) { log.error(e.getMessage()); handler.processError(e); } catch (IndexOutOfBoundsException e) { } } } /** * ??IPort. * ???//. * @see IPort#send(ByteBuffer) * @see IPort#receive(ByteBuffer) * @see IPort#maintain() */ @Override public void run() { log.debug("Porter??"); int n = 0; while (!shutdown) { // do select try { n = selector.select(3000); // ?shutdownselector if (shutdown) { selector.close(); break; } } catch (IOException e) { log.error(e.getMessage()); dispatchErrorToAll(e); } // ? processDisposeQueue(); // select0? if (n > 0) { for (Iterator<SelectionKey> i = selector.selectedKeys().iterator(); i.hasNext();) { // Key SelectionKey sk = i.next(); i.remove(); // ? if (!sk.isValid()) continue; // ? INIOHandler handler = (INIOHandler) sk.attachment(); try { if (sk.isConnectable()) handler.processConnect(sk); else if (sk.isReadable()) handler.processRead(sk); } catch (IOException e) { log.error(e.getMessage()); handler.processError(e); } catch (PacketParseException e) { log.debug("?: " + e.getMessage()); } catch (RuntimeException e) { log.error(e.getMessage()); } } n = 0; } checkNewConnection(); notifySend(); } selector = null; shutdown = false; log.debug("Porter?"); } /** * * * @param p */ public void addDisposeRequest(IPort p) { synchronized (disposeQueue) { disposeQueue.offer(p); } } /** * * * @param p */ public void addDisposeRequest(IProxy p) { synchronized (disposeQueue) { disposeQueue.offer(p); } } /** * ?? */ private void checkNewConnection() { while (!newConnections.isEmpty()) { Object handler = newConnections.remove(0); if (handler instanceof IProxy) { try { register((IProxy) handler); } catch (ClosedChannelException e1) { } } else if (handler instanceof IPort) { try { register((IPort) handler); } catch (ClosedChannelException e1) { } } } } /** * ? */ private void processDisposeQueue() { synchronized (disposeQueue) { while (!disposeQueue.isEmpty()) { Object obj = disposeQueue.poll(); if (obj instanceof IPort) deregister((IPort) obj); else if (obj instanceof IProxy) deregister((IProxy) obj); } } } /** * porter */ public void shutdown() { if (selector != null) { shutdown = true; selector.wakeup(); } } /** * selector */ public void wakeup() { selector.wakeup(); } /** * selector?proxy * * @param proxy */ public void wakeup(Object handler) { newConnections.add(handler); selector.wakeup(); } }