WiFi和热点开发——tcp连接查看实时日志

本人从事多年的Android智能设备开发,做过手机、MiFi、智能门锁等产品,除了手机之外,其他的产品在后期的维护及版本迭代过程中,经常需要通过分析查看设备的日志来定位问题,比如智能门锁,遇到故障时,经常就抱着一台笔记本,打开设备的usb调试开关后,用usb线连接设备进行问题重现和日志分析。当然,获取日志的方式是多种多样的,也可以通过网络上传。但总免不了需要现场查看,现场查看就有个问题,笔记本并不是随身携带,而手机确实随身携带的,因此,能不能在手机端实时查看设备的日志呢?答案是肯定的。
MiFi和智能门锁都开放了热点,因此,调试的手机可以连接到设备的热点,进行组网后实现实时日志查看。这种方式既不需要数据线,也不需要笔记本,甚至usb调试开关也不用开启。
先说服务端(设备端),注意这里用到了线程池,因此服务端是具备了一对多进行响应的基础能力的:

    /**
     * @作用 开启tcp服务
     */
    public void startTCP() {
        if (runnableTcp != null) {
            isStart = false;
            runnableTcp = null;
        }
        try {
            runnableTcp = new TcpReceive();
            threadTcp = new Thread(runnableTcp);
            isStart = true;
            threadTcp.start();
        } catch (Exception e) {
            logd("开启TCP失败:" + e.getMessage());
        }

        try {
            mExe = Executors.newCachedThreadPool();// 创建一个线程池
        } catch (Exception e) {
            logd("创建线程池失败:" + e);
        }
    }

    private class TcpReceive implements Runnable {
        Socket socket = null;
        ServerSocket server = null;

        public void run() {
            try {
                //server = new ServerSocket(53858);
                if (server == null) {
                    server = new ServerSocket();
                    server.setReuseAddress(true);
                    server.bind(new InetSocketAddress(53858));
                }
                allSockets = new HashSet();
                while (isRunning) {
                    if (!server.isClosed()) {
                        //logd("serverSocket "+server.hashCode()+" 监听53858端口中...");
                        socket = server.accept();
                    }
                    if (socket != null) {
                        allSockets.add(socket);
                        //logd("新的socket " + socket.hashCode() + " 加入,当前socket总数为:" + allSockets.size());
                        mExe.execute(new EchoThread(socket));
                    }

                }
            } catch (IOException e) {
                logd("serverSocket创建异常"+e);
            }
        }
    }

    public class EchoThread extends Thread {
        Socket socket;
        PrintWriter out;        
        BufferedReader in;

        public EchoThread(Socket _socket){
            socket = _socket;
            try {
                out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
                in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            } catch (IOException e) {
                logd("PrintWriter或BufferedReader异常"+e); 
            }
        }

        public void run() {
            try {
                while (isStart) {               
                    if (socket == null || socket.isClosed() || !socket.isBound() || !socket.isConnected()) {
                        break;
                    }
                    String line = in.readLine();
                    String ip = socket.getInetAddress().toString();

                    if (ip.contains("/")) {
                        ip = ip.substring(1, ip.length());
                    }
                    if (line == null){
                        logd("line请求为空");
                        break;
                    }
                    String temp[] = line.split("/");
                    if(temp == null || temp.length <2 ||!temp[0].equals(TcpConfig._HEAD_)){
                        return;
                    }else{
                        line = temp[1];
                    }

                    if (!line.equals(TcpConfig.GET_DEVICE_INFO) && !line.contains(TcpConfig.RESTART_TEST_GET_RES)){
                        logd("Tcp收到app请求消息为:" + line + ";app对应手机的ip为:" + ip);
                    }
                    if (line.equals(TcpConfig.GET_REAL_TIME_LOGS)) {
                        isRealTimeLogs = true;
                        App.getInstance().setRealTimeLogQueque(mQueue);

                        String logs = null;
                        while (isRealTimeLogs) {
                            try {
                                logs = mQueue.take();
                                out.println(logs);
                                Thread.sleep(20);
                            } catch (Exception e) {
                                e.printStackTrace();
                                logd(e);
                            }
                        }

                        out.print("false,quit.");
                    }
                    if (!isCmds(line)) {
                        out.println("Request Error");
                    }
                    out.flush();
                    out.close();
                    socket.close();
                    allSockets.remove(socket);  
                }
            } catch (IOException e) {
                e.printStackTrace();
                logd("socket信息交互异常:" + e);
            } finally {
                try {
                    if (in != null) {
                        // logd("BufferedReader关闭,hashCode:" +  in.hashCode());
                        in.close();
                    }
                    if (out != null) {
                        // logd("PrintWriter关闭,hashCode:" + out.hashCode());
                        out.close();
                    }
                    if (socket != null) {
                        // logd("socket "+socket.hashCode()+" 关闭");
                        socket.close();
                        allSockets.remove(socket);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

代码中已经略去了其他的设备控制接口,注意实时日志查看的接口和退出实时查看的接口:

if (line.equals(TcpConfig.GET_REAL_TIME_LOGS)) {
    isRealTimeLogs = true;
    App.getInstance().setRealTimeLogQueque(mQueue);

    String logs = null;
    while (isRealTimeLogs) {
        try {
            logs = mQueue.take();
            out.println(logs);
            Thread.sleep(20);
        } catch (Exception e) {
            e.printStackTrace();
            logd(e);
        }
    }

    out.print("false,quit.");
}

if(line.equals(TcpConfig.CANCEL_REAL_TIME_LOGS)){
    isRealTimeLogs = false;
    mQueue.clear();
    App.getInstance().setRealTimeLogQueque(null);
    out.print(true);
}

其实就是从队列里面循环取日志内容往对端发送。
那么日志是在哪里加入队列的呢?我把记录日志的方法写在了Application中,整个工程记录日志最终都会走到这个方法。那么在记录日志的同时就加入到该队列就可以了。

    /**
     * @作用 把传递消息写进Log日志
     */
    public static void showTestInfo(final Object msg) {
        if (msg == null || msg.toString().isEmpty()) {
            return;
        }
        String appMsg = getDateToStringStyle("MM-dd HH:mm:ss,SSS", new Date()) + ":" + msg.toString();
        if (logger == null) {
            logger = Logger.getLogger(TGTConfig.TAG);
        }
        logger.warn(appMsg);

        if(mQueue != null){
            mQueue.add(appMsg);
        }
    }

这个队列的在收到查看实时日志指令时初始化,在退出实时日志查看时置空(日志不会加入队列):

private static PriorityBlockingQueue mQueue;

public void setRealTimeLogQueque(PriorityBlockingQueue queue){
        mQueue = queue;
}

服务端就是这样,接下来看客户端:

private void startRealTimeLog(){
        cmd = TcpConfig.GET_REAL_TIME_LOGS;
        new Thread(sendTcp).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                doloop();
            }
        }).start();
    }

    private void stopRealTimeLog(){
        cmd = TcpConfig.CANCEL_REAL_TIME_LOGS;
        new Thread(sendTcp).start();
    }

    private void doloop(){
        String res = null;
        while (isStart){
            try{
                res = mQueue.take();
                addIntoList(res);
                Thread.sleep(50);
            }catch (Exception e){
                e.printStackTrace();
                logd(e);
            }
        }
        logd("loop quit.");
    }

    private void addIntoList(String string){
        if (realTimeLogs.size() > MAX_LINE) {
            realTimeLogs.removeFirst();
        }
        realTimeLogs.add(string);
        uiHandler.removeMessages(MSG_REFRESH_UI);
        uiHandler.sendMessageDelayed(Message.obtain(uiHandler,MSG_REFRESH_UI,0,0,realTimeLogs),100);
    }

    Runnable sendTcp = new Runnable() {
        @Override
        public void run() {
            BufferedReader in = null;
            PrintWriter out = null;
            Socket socket = new Socket();
            try {
                socket.connect(new InetSocketAddress(TcpUtil.hostIP, TcpConfig.port), 10 * 1000);
                if (!cmd.isEmpty()) {
                    while (socket.isConnected() && !socket.isClosed()) {
                        try {
                            out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
                            out.println(TcpConfig._HEAD_ + "/" + cmd);
                            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                            String line = null;
                            while ((line = in.readLine()) != null) {
                                logd(line);
                                mQueue.add(line);
                            }
                            break;
                        } catch (Exception e) {
                            e.printStackTrace();
                            logd(e);
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                logd(e);
            } finally {
                try {
                    if (in != null)
                        in.close();
                    if (socket != null)
                        socket.close();
                    if (out != null)
                        out.close();
                } catch (IOException e) {
                    logd(e);
                }
            }
        }
    };

其实你会发现,并么有多复杂,一个线程阻塞地去读取服务端返回的数据,另一个线程循环的从队列读取数据,并发送到UI线程进行UI刷新。

你可能感兴趣的:(原创)