Package org.nutz.lang.socket

Source Code of org.nutz.lang.socket.Sockets

package org.nutz.lang.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.Streams;
import org.nutz.lang.born.Borning;
import org.nutz.lang.util.Context;
import org.nutz.log.Log;
import org.nutz.log.Logs;

public abstract class Sockets {

    private static final Log log = Logs.get();

    /**
     * 向某主机发送一些字节内容,并将返回写入输出流
     *
     * @param host
     *            主机
     * @param port
     *            端口
     * @param ins
     *            发送的内容
     * @param ops
     *            主机返回的输入流
     */
    public static void send(String host, int port, InputStream ins, OutputStream ops) {
        Socket socket = null;
        try {
            socket = new Socket(InetAddress.getByName(host), port);
            // 发送关闭命令
            OutputStream sOut = socket.getOutputStream();
            Streams.write(sOut, ins);
            sOut.flush();

            // 接收服务器的反馈
            if (!socket.isClosed()) {
                InputStream sReturn = socket.getInputStream();
                Streams.write(ops, sReturn);
            }
        }
        catch (IOException e) {
            throw Lang.wrapThrow(e);
        }
        finally {
            Streams.safeClose(ins);
            Streams.safeClose(ops);
            safeClose(socket);
        }
    }

    /**
     * 向某主机发送一段文本,并将主机的返回作为文本返回
     *
     * @param host
     *            主机
     * @param port
     *            端口
     * @param text
     *            发送的内容
     * @return 主机返回的文本
     */
    public static String sendText(String host, int port, String text) {
        StringBuilder sb = new StringBuilder();
        send(host, port, Lang.ins(text), Lang.ops(sb));
        return sb.toString();
    }

    /**
     * 监听本地某一个端口,仅仅收到某一个特殊命令时,才会开始一个动作。
     * <p>
     * 并且原生的,它支持输入 "close|stop|bye|exit" 来结束本地监听
     *
     * @param port
     *            要监听的端口
     * @param line
     *            命令名称
     * @param action
     *            动作执行类
     */
    public static void localListenOneAndStop(int port, String line, SocketAction action) {
        Map<String, SocketAction> actions = createActions();
        actions.put(line, action);
        actions.put("$:^(close|stop|bye|exit)$", doClose());
        localListenByLine(port, actions);
    }

    /**
     * 监听本地某一个端口,仅仅收到某一个特殊命令时,才会开始一个动作。
     *
     * @param port
     *            要监听的端口
     * @param line
     *            命令名称
     * @param action
     *            动作执行类
     */
    public static void localListenOne(int port, String line, SocketAction action) {
        Map<String, SocketAction> actions = createActions();
        actions.put(line, action);
        localListenByLine(port, actions);
    }

    /**
     * 对于一个 CPU 默认起的处理线程数
     */
    private static final int DEFAULT_POOL_SIZE = 10;

    /**
     * 简化了一个参数,采用默认线程数
     *
     * @see org.nutz.lang.socket.Sockets#localListenByLine(int, Map, int)
     */
    public static void localListenByLine(int port, Map<String, SocketAction> actions) {
        Sockets.localListenByLine(port, actions, DEFAULT_POOL_SIZE);
    }

    /**
     * 监听本地某一个端口,根据用户输入的命令的不同,执行不同的操作
     * <p>
     * 当然,你如果想让一个过程处理多种命令,请给的 key 前用 "REGEX:" 作为前缀,后面用一个正则表达式 来表示你的你要的匹配的命令 <br>
     * "REGEX:!" 开头的,表示后面的正则表达式是一个命令过滤,所有没有匹配上的命令都会被处理
     *
     * @param port
     *            要监听的端口
     * @param actions
     *            动作执行类映射表
     * @param poolSize
     *            针对一个 CPU 你打算启动几个处理线程
     *
     * @see org.nutz.lang.socket.Sockets#localListenByLine(int, Map,
     *      ExecutorService)
     */
    public static void localListenByLine(int port, Map<String, SocketAction> actions, int poolSize) {
        Sockets.localListenByLine(    port,
                                    actions,
                                    Executors.newFixedThreadPool(Runtime.getRuntime()
                                                                        .availableProcessors()
                                                                    * poolSize));
    }

    /**
     * 监听本地某一个端口,根据用户输入的命令的不同,执行不同的操作
     * <p>
     * 当然,你如果想让一个过程处理多种命令,请给的 key 前用 "REGEX:" 作为前缀,后面用一个正则表达式 来表示你的你要的匹配的命令 <br>
     * "REGEX:!" 开头的,表示后面的正则表达式是一个命令过滤,所有没有匹配上的命令都会被处理
     *
     * @param port
     *            要监听的端口
     * @param actions
     *            动作执行类映射表
     * @param service
     *            线程池的实现类
     */
    public static void localListenByLine(    int port,
                                            Map<String, SocketAction> actions,
                                            ExecutorService service) {
        localListen(port, actions, service, SocketAtom.class);
    }

    /**
     * 监听本地某一个端口,根据用户输入的命令的不同,执行不同的操作
     * <p>
     * 当然,你如果想让一个过程处理多种命令,请给的 key 前用 "REGEX:" 作为前缀,后面用一个正则表达式 来表示你的你要的匹配的命令 <br>
     * "REGEX:!" 开头的,表示后面的正则表达式是一个命令过滤,所有没有匹配上的命令都会被处理
     *
     * @param port
     *            要监听的端口
     * @param actions
     *            动作执行类映射表
     * @param service
     *            线程池的实现类
     */
    @SuppressWarnings("rawtypes")
    public static void localListen(    int port,
                                    Map<String, SocketAction> actions,
                                    ExecutorService service,
                                    Class<? extends SocketAtom> klass) {
        try {
            // 建立动作映射表
            SocketActionTable saTable = new SocketActionTable(actions);

            // 初始化 socket 接口
            final ServerSocket server;
            try {
                server = new ServerSocket(port);
            }
            catch (IOException e1) {
                throw Lang.wrapThrow(e1);
            }

            if (log.isInfoEnabled())
                log.infof("Local socket is up at :%d with %d action ready", port, actions.size());

            final Context context = Lang.context();
            context.set("stop", false);
            /*
             * 启动一个守护线程,判断是否该关闭 socket 服务
             */
            (new Thread() {
                @Override
                public void run() {
                    setName("Nutz.Sockets monitor thread");
                    while (true) {
                        try {
                            Thread.sleep(1000);
                            if (context.getBoolean("stop")) {
                                try {
                                    server.close();
                                }
                                catch (Throwable e) {}
                                return;
                            }
                        }
                        catch (Throwable e) {}
                    }
                }
            }).start();
            /*
             * 准备 SocketAtom 的生成器
             */
            Borning borning = Mirror.me(klass).getBorningByArgTypes(Context.class,
                                                                    Socket.class,
                                                                    SocketActionTable.class);
            if (borning == null) {
                log.error("boring == null !!!!");
                return;
            }
            /*
             * 进入监听循环
             */
            while (!context.getBoolean("stop")) {
                try {
                    if (log.isDebugEnabled())
                        log.debug("Waiting for new socket");
                    Socket socket = server.accept();
                    if (context.getBoolean("stop")) {
                        Sockets.safeClose(socket);
                        break;// 监护线程也许还是睡觉,还没来得及关掉哦,所以自己检查一下
                    }
                    if (log.isDebugEnabled())
                        log.debug("accept a new socket, create new SocketAtom to handle it ...");
                    Runnable runnable = (Runnable) borning.born(new Object[]{    context,
                                                                                socket,
                                                                                saTable});
                    service.execute(runnable);
                }
                catch (Throwable e) {
                    log.info("Throwable catched!! maybe ask to exit", e);
                }
            }

            if (!server.isClosed()) {
                try {
                    server.close();
                }
                catch (Throwable e) {}
            }

            log.info("Seem stop signal was got, wait 15 for all running thread");

            try {
                service.shutdown();
                service.awaitTermination(15, TimeUnit.SECONDS);
            }
            catch (InterruptedException e) {}
            try {
                service.shutdownNow();
            }
            catch (Throwable e2) {}
        }
        catch (RuntimeException e) {
            throw e;
        }
        finally {
            if (log.isInfoEnabled())
                log.info("Stop services ...");
            service.shutdown();
        }

        if (log.isInfoEnabled())
            log.infof("Local socket is down for :%d", port);

    }

    /**
     * 安全关闭套接层,容忍 null
     *
     * @param socket
     *            套接层
     * @return 一定会返回 null
     */
    public static Socket safeClose(Socket socket) {
        if (null != socket)
            try {
                socket.close();
                socket = null;
            }
            catch (IOException e) {
                throw Lang.wrapThrow(e);
            }
        return null;
    }

    /**
     * 创建一个停止监听的动作对象
     *
     * @return 动作对象
     */
    public static SocketAction doClose() {
        return new SocketAction() {
            public void run(SocketContext context) {
                throw new CloseSocketException();
            }
        };
    }

    /**
     * 这个函数可以在你的 SocketAction 实现类里被调用,用来关闭当前的监听星闻
     */
    public static void close() {
        throw new CloseSocketException();
    }

    /**
     * 快捷创建动作映射表的方法
     *
     * @return 动作映射表
     */
    public static Map<String, SocketAction> createActions() {
        Map<String, SocketAction> actions = new HashMap<String, SocketAction>();
        return actions;
    }

}
TOP

Related Classes of org.nutz.lang.socket.Sockets

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.