首页 > Web开发 > 详细

网页主动探测工具(修改Bug)

时间:2014-11-29 02:18:53      阅读:362      评论:0      收藏:0      [点我收藏+]
http://blog.itpub.net/29254281/viewspace-1344706/

程序的结构虽然定了,但是程序本身还有一些bug
首先ParseHandler的解析有些问题,原来的程序是这样的
bubuko.com,布布扣
路径中有冒号的被过滤掉了,但是这样会把绝对路径也过滤掉.
其实最开始的本意是过滤如下的地址"mvboxmtv:url=http://d.mvbox.cn/util/getPlayer.htm?appId=9306"

针对这个问题,在Task类中增加了isValid字段,然后在Default过滤器中处理,
在过滤器的最后一个链条,如果还有冒号,则将Task的valid标识为false
在createNewTask函数的过滤器结束的时候,只有Task valid为true的对象,才会再次放入连接队列
bubuko.com,布布扣  

另一个修正是,Task对象的setCurrentPath方法
这个方法会截取后缀,如"test.html?name=xx"或者“test.html”
他都可以正确的截取到后缀 html
但是我忽略了一种情况,“test.html#category”
还有井号的情况.
修正如下
bubuko.com,布布扣

还有两个错误是关于JAVA理解的问题.
在解析的处理中使用了责任链模式,
bubuko.com,布布扣
最开始的设计是将Task对象推入责任链,如果在责任链中判断不符合条件,将对象设置为null
想用null作为一个判断是否正确的状态位
仔细想想就能明白这是一个非常初级的错误.
责任链函数内的指针作用域相当于本地变量.所以外层的函数一直引用着Task对象,无论责任链的函数如何处理.
解决这个问题倒是很容易,在Task类增加isValid状态标志就可以了.

另外还有一个并发的错误,
最开始设计,是希望一个并发容器可以存放已经访问的链接,避免环路访问导致不能结束.
private static Set<String> SET = new ConcurrentSkipListSet<String>();

但是解析的handler方法,犯了一个关于并发的典型错误.
bubuko.com,布布扣

虽然容器的contains方法是线程安全的,但是上面的这段代码却是不是线程安全的
假设一个线程通过了contains方法,还没有执行到SET.add方法,但是就在同时,另外的线程进入到了SET.contains方法,他得到的结果也肯定是通过的.
所以一个链接可能被执行了两次.

新的程序增加了一个异常处理,如果Socket超时,会将这个Task对象重新放入连接队列.



修改之后的代码如下:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Probe {

    private static final BlockingQueue<Task> CONNECTLIST = new LinkedBlockingQueue<Task>();
    private static final BlockingQueue<Task> PARSELIST = new LinkedBlockingQueue<Task>();
    private static final BlockingQueue<Task> PERSISTENCELIST = new LinkedBlockingQueue<Task>();
    private static ExecutorService CONNECTTHREADPOOL;
    private static ExecutorService PARSETHREADPOOL;
    private static ExecutorService PERSISTENCETHREADPOOL;
    private static final List<String> DOMAINLIST = new CopyOnWriteArrayList<>();

    static {
        CONNECTTHREADPOOL = Executors.newFixedThreadPool(100);
        PARSETHREADPOOL = Executors.newSingleThreadExecutor();
        PERSISTENCETHREADPOOL = Executors.newFixedThreadPool(1);
        DOMAINLIST.add("news.163.com");
    }

    public static void main(String args[]) throws Exception {
        long start = System.currentTimeMillis();
        CONNECTLIST.put(new Task("news.163.com", 80, "/index.html"));

        for (int i = 0; i < 120; i++) {
            CONNECTTHREADPOOL
                    .submit(new ConnectHandler(CONNECTLIST, PARSELIST));
        }
        PARSETHREADPOOL.submit(new ParseHandler(CONNECTLIST, PARSELIST,
                PERSISTENCELIST, DOMAINLIST));
        PERSISTENCETHREADPOOL.submit(new PersistenceHandler(PERSISTENCELIST));

        while (true) {
            Thread.sleep(1000);
            long end = System.currentTimeMillis();
            float interval = ((end - start) / 1000);
            int connectTotal = ConnectHandler.GETCOUNT();
            int parseTotal = ParseHandler.GETCOUNT();
            int persistenceTotal = PersistenceHandler.GETCOUNT();

            int connectps = Math.round(connectTotal / interval);
            int parseps = Math.round(parseTotal / interval);
            int persistenceps = Math.round(persistenceTotal / interval);
            System.out.print("\r连接总数:" + connectTotal + " \t每秒连接:" + connectps
                    + "\t连接队列剩余:" + CONNECTLIST.size() + " \t解析总数:"
                    + parseTotal + " \t每秒解析:" + parseps + "\t解析队列剩余:"
                    + PARSELIST.size() + " \t持久化总数:" + persistenceTotal
                    + " \t每秒持久化:" + persistenceps + "\t持久化队列剩余:"
                    + PERSISTENCELIST.size());
        }
    }
}

class Task {
    public Task() {
    }

    public void init(String host, int port, String path) {
        this.setCurrentPath(path);
        this.host = host;
        this.port = port;
    }

    public Task(String host, int port, String path) {
        init(host, port, path);
    }

    private String host;
    private int port;
    private String currentPath;
    private long taskTime;
    private String type;
    private String content;
    private int state;
    private boolean isValid = true;

    public boolean isValid() {
        return isValid;
    }

    public void setValid(boolean isValid) {
        this.isValid = isValid;
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public String getCurrentPath() {
        return currentPath;
    }

    public void setCurrentPath(String currentPath) {
        this.currentPath = currentPath;
        int i = 0;
        if (currentPath.indexOf("?") != -1) {
            i = currentPath.indexOf("?");
        } else {
            if (currentPath.indexOf("#") != -1) {
                i = currentPath.indexOf("#");
            } else {
                i = currentPath.length();
            }
        }
        this.type = currentPath.substring(currentPath.indexOf(".") + 1, i);
    }

    public long getTaskTime() {
        return taskTime;
    }

    public void setTaskTime(long taskTime) {
        this.taskTime = taskTime;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;

    }
}

class ParseHandler implements Runnable {
    private static final Set<String> SET = new HashSet<String>();

    public static int GETCOUNT() {
        return COUNT.get();
    }

    private static final AtomicInteger COUNT = new AtomicInteger();
    private BlockingQueue<Task> connectlist;
    private BlockingQueue<Task> parselist;
    private BlockingQueue<Task> persistencelist;
    List<String> domainlist;

    private interface Filter {
        void doFilter(Task fatherTask, Task newTask, String path, Filter chain);
    }

    private class FilterChain implements Filter {
        private List<Filter> list = new ArrayList<Filter>();

        {
            addFilter(new TwoLevel());
            addFilter(new OneLevel());
            addFilter(new FullPath());
            addFilter(new Root());
            addFilter(new Default());
        }

        private void addFilter(Filter filter) {
            list.add(filter);
        }

        private Iterator<Filter> it = list.iterator();

        @Override
        public void doFilter(Task fatherTask, Task newTask, String path,
                Filter chain) {
            if (it.hasNext()) {
                it.next().doFilter(fatherTask, newTask, path, chain);
            }
        }

    }

    private class TwoLevel implements Filter {

        @Override
        public void doFilter(Task fatherTask, Task newTask, String path,
                Filter chain) {
            if (path.startsWith("../../")) {
                String prefix = getPrefix(fatherTask.getCurrentPath(), 3);
                newTask.init(fatherTask.getHost(), fatherTask.getPort(),
                        path.replace("../../", prefix));
            } else {
                chain.doFilter(fatherTask, newTask, path, chain);
            }

        }
    }

    private class OneLevel implements Filter {

        @Override
        public void doFilter(Task fatherTask, Task newTask, String path,
                Filter chain) {
            if (path.startsWith("../")) {
                String prefix = getPrefix(fatherTask.getCurrentPath(), 2);
                newTask.init(fatherTask.getHost(), fatherTask.getPort(),
                        path.replace("../", prefix));
            } else {
                chain.doFilter(fatherTask, newTask, path, chain);
            }

        }

    }

    private class FullPath implements Filter {

        @Override
        public void doFilter(Task fatherTask, Task newTask, String path,
                Filter chain) {
            if (path.startsWith("http://")) {
                Iterator<String> it = domainlist.iterator();
                boolean flag = false;
                while (it.hasNext()) {
                    String domain = it.next();
                    if (path.startsWith("http://" + domain + "/")) {
                        newTask.init(domain, fatherTask.getPort(),
                                path.replace("http://" + domain + "/", "/"));
                        flag = true;
                        break;
                    }
                }
                if (!flag) {
                    newTask.setValid(false);
                }
            } else {
                chain.doFilter(fatherTask, newTask, path, chain);
            }
        }

    }

    private class Root implements Filter {

        @Override
        public void doFilter(Task fatherTask, Task newTask, String path,
                Filter chain) {
            if (path.startsWith("/")) {
                newTask.init(fatherTask.getHost(), fatherTask.getPort(), path);
            } else {
                chain.doFilter(fatherTask, newTask, path, chain);
            }
        }

    }

    private class Default implements Filter {

        @Override
        public void doFilter(Task fatherTask, Task newTask, String path,
                Filter chain) {
            if (path.contains(":")) {
                newTask.setValid(false);
                return;
            }
            String prefix = getPrefix(fatherTask.getCurrentPath(), 1);
            newTask.init(fatherTask.getHost(), fatherTask.getPort(), prefix
                    + "/" + path);
        }
    }

    public ParseHandler(BlockingQueue<Task> connectlist,
            BlockingQueue<Task> parselist, BlockingQueue<Task> persistencelist,
            List<String> domainlist) {
        this.connectlist = connectlist;
        this.parselist = parselist;
        this.persistencelist = persistencelist;
        this.domainlist = domainlist;
    }

    private Pattern pattern = Pattern.compile("\"[^\"]+\\.htm[^\"]*\"");

    private void handler() {
        try {
            Task task = parselist.take();
            parseTaskState(task);
            if (200 == task.getState()) {
                Matcher matcher = pattern.matcher(task.getContent());
                while (matcher.find()) {
                    String path = matcher.group();
                    if (!path.contains(" ") && !path.contains("\t")
                            && !path.contains("(") && !path.contains(")")) {
                        path = path.substring(1, path.length() - 1);

                        createNewTask(task, path);
                    }
                }
            }
            task.setContent(null);
            persistencelist.put(task);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void parseTaskState(Task task) {
        if (task.getContent().startsWith("HTTP/1.1")) {
            task.setState(Integer.parseInt(task.getContent().substring(9, 12)));
        } else {
            task.setState(Integer.parseInt(task.getContent().substring(19, 22)));
        }
    }

    /**
     * @param fatherTask
     * @param path
     * @throws Exception
     */
    private void createNewTask(Task fatherTask, String path) throws Exception {
        Task newTask = new Task();
        FilterChain filterchain = new FilterChain();
        filterchain.doFilter(fatherTask, newTask, path, filterchain);
        if (newTask.isValid()) {
            synchronized (SET) {
                if (SET.contains(newTask.getHost() + newTask.getCurrentPath())) {
                    return;
                }
                SET.add(newTask.getHost() + newTask.getCurrentPath());
            }
            connectlist.put(newTask);
        }
    }

    private String getPrefix(String s, int count) {
        String prefix = s;
        while (count > 0) {
            prefix = prefix.substring(0, prefix.lastIndexOf("/"));
            count--;
        }
        return "".equals(prefix) ? "/" : prefix;
    }

    @Override
    public void run() {
        while (true) {
            this.handler();
            COUNT.addAndGet(1);
        }
    }

}

class ConnectHandler implements Runnable {
    public static int GETCOUNT() {
        return COUNT.get();
    }

    private static final AtomicInteger COUNT = new AtomicInteger();
    private BlockingQueue<Task> connectlist;
    private BlockingQueue<Task> parselist;

    public ConnectHandler(BlockingQueue<Task> connectlist,
            BlockingQueue<Task> parselist) {
        this.connectlist = connectlist;
        this.parselist = parselist;
    }

    private void handler() {
        Task task = null;
        try {
            task = connectlist.take();
            long start = System.currentTimeMillis();
            getHtml(task);
            long end = System.currentTimeMillis();
            task.setTaskTime(end - start);
            parselist.put(task);
        } catch (SocketException e) {
            if (task != null) {
                try {
                    connectlist.put(task);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void getHtml(Task task) throws Exception {
        StringBuilder sb = new StringBuilder(2048);
        InetAddress addr = InetAddress.getByName(task.getHost());

        // 建立一个Socket

        Socket socket = new Socket(addr, task.getPort());

        // 发送命令,无非就是在Socket发送流的基础上加多一些握手信息,详情请了解HTTP协议
        BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(
                socket.getOutputStream(), "UTF-8"));
        wr.write("GET " + task.getCurrentPath() + " HTTP/1.0\r\n");
        wr.write("HOST:" + task.getHost() + "\r\n");
        wr.write("Accept:*/*\r\n");
        wr.write("\r\n");
        wr.flush();

        // 接收Socket返回的结果,并打印出来
        BufferedReader rd = new BufferedReader(new InputStreamReader(
                socket.getInputStream()));
        String line;
        while ((line = rd.readLine()) != null) {
            sb.append(line);
        }
        wr.close();
        rd.close();
        task.setContent(sb.toString());
        socket.close();
    }

    @Override
    public void run() {
        while (true) {
            this.handler();
            COUNT.addAndGet(1);
        }
    }
}

class PersistenceHandler implements Runnable {
    static {
        try {
            Class.forName("oracle.jdbc.OracleDriver");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static int GETCOUNT() {
        return COUNT.get();
    }

    private static final AtomicInteger COUNT = new AtomicInteger();
    private BlockingQueue<Task> persistencelist;

    public PersistenceHandler(BlockingQueue<Task> persistencelist) {
        this.persistencelist = persistencelist;
        try {
            conn = DriverManager.getConnection(
                    "jdbc:oracle:thin:127.0.0.1:1521:orcl", "edmond", "edmond");
            ps = conn
                    .prepareStatement("insert into probe(id,host,path,state,tasktime,type) values(seq_probe_id.nextval,?,?,?,?,?)");
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private Connection conn;
    private PreparedStatement ps;

    @Override
    public void run() {
        while (true) {
            this.handler();
            COUNT.addAndGet(1);
        }
    }

    private void handler() {
        try {
            Task task = persistencelist.take();
            ps.setString(1, task.getHost());
            ps.setString(2, task.getCurrentPath());
            ps.setInt(3, task.getState());
            ps.setLong(4, task.getTaskTime());
            ps.setString(5, task.getType());

            ps.executeUpdate();
            conn.commit();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

这个程序还有一些优化的空间,
比如他现在不能自动停止,
并且采用了一个Socket绑定一个线程的方式,耗费了大量的线程资源.
很多异常处理也不是很周全.
会慢慢完善的.

网页主动探测工具(修改Bug)

原文:http://blog.itpub.net/29254281/viewspace-1347985/

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!