码迷,mamicode.com
首页 > 编程语言 > 详细

多线程批量探测目标IP段的服务器类型(内网也可用)

时间:2015-12-13 11:12:22      阅读:281      评论:0      收藏:0      [点我收藏+]

标签:java   线程池   服务器类型   内网   

一 原理解释

这里所说的服务器类型是指像Apache,tomcat,nginx,IIS这种。其中原理用到了HTTP Header的Responses,这里面有项叫“Server”的参数就包涵我们所需要的信息。下面是Responses的部分截图:

技术分享

(PS:更多相关可自行百度“HTTP Header”)

因此,我们想要做一个多线程批量探测的软件,思路有两种:1)根据别人提供的接口然后我们去调用获取(比如:http://api.builtwith.com 这个我以后可能会写);(2)针对每个IP我们发送Get请求,然后去获取响应头文件中的Server参数

PS:文末我会放出打包好的有GUI界面的jar文件以及完整源代码

二 项目结构

这里我选择了第二种方式,自己动手做一个,虽然获取到的信息没有用接口获取的来的全。下面是整个完整小项目的目录结构:

技术分享


三 核心代码

在这里核心代码在ServerTypeDemo.java这个文件中,主要是通过对指定IP以及端口发出Get请求,然后获取响应包中的“Server”,最后将结果写入文件。代码如下:

package action;

import java.io.BufferedWriter;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.Map;

public class ServerTypeDemo{	
	/**
	 * 获取到的服务器类型写入txt
	 * @param ip IP
	 * @param port 提交端口
	 * @param writer 写入流
	 * 
	 * @return null
	 * */
	public static void savaData(String ip,String port,BufferedWriter writer){
		try {
			URL url = new URL("http://" + ip + ":" + port);
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("GET");
			connection.setConnectTimeout(1000);  //毫秒
			connection.setReadTimeout(1000);
			Map<String, List<String>> map = connection.getHeaderFields();  //获取HTTP Header Responses
			
			List<String> server = map.get("Server");  //关键点
			if(server == null){
				return;
			}
			else{	
				//写入文件
				for(String tmp : server){
					writer.write(ip + ":" + port + "       " + tmp);
					writer.newLine();					
				}
				writer.flush();
				connection.disconnect();
			}
		
		} catch (MalformedURLException e) {			
			e.printStackTrace();
		} catch (IOException e) {			
			e.printStackTrace();
		}

	}
	
}


四 一个陋界

技术分享

从左到右分别填:起始IP,结束IP(PS:在这里两个IP不在一个C段也行,比如:192.168.1.1~192.168.255.255),线程数,最后点击开始进行扫描,待全部线程结束后会给出提示信息。界面相关代码如下:

package view;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

import util.IPTraverse;

import action.MyThread;

public class MainView extends JFrame implements ActionListener {
	/**
	 * 此程序是为了批量探测目标IP段的服务器类型(Apache,tomcat,nginx,IIS。。。) 其中用到了线程池,可以自定义扫描线程数量
	 * (PS:只做了一个简陋的界面 O(∩_∩)O~)
	 * 
	 * @author zifangsky
	 * @blog http://www.zifangsky.cn
	 * @version V1.0.0
	 * @date 2015-12-9
	 * */
	private static final long serialVersionUID = 1L;
	private JPanel mainJPanel;
	private JTextField start, end, threadNum; // 起始IP,结束IP,线程值
	private JButton submit;

	private JMenuBar jMenuBar;
	private JMenu help; // 帮助
	private JMenuItem author, contact, version, readme; // 作者,邮箱,版本号,使用说明

	private Font font = new Font("宋体", Font.LAYOUT_NO_LIMIT_CONTEXT, 16);

	public MainView() {
		super("批量判断服务器类型(Apache,tomcat,nginx。。。) by zifangsky");
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // 屏幕大小
		setPreferredSize(new Dimension(600, 300));
		int frameWidth = this.getPreferredSize().width; // 界面宽度
		int frameHeight = this.getPreferredSize().height; // 界面高度
		setSize(frameWidth, frameHeight);
		setLocation((screenSize.width - frameWidth) / 2,
				(screenSize.height - frameHeight) / 2);

		mainJPanel = new JPanel();
		start = new JTextField("192.168.1.1", 12);
		end = new JTextField("192.168.1.254", 12);
		threadNum = new JTextField("5", 8);
		submit = new JButton("开始");
		submit.setFont(font);

		jMenuBar = new JMenuBar();
		help = new JMenu("帮助");
		help.setFont(font);
		author = new JMenuItem("作者");
		author.setFont(font);
		contact = new JMenuItem("联系方式");
		contact.setFont(font);
		version = new JMenuItem("版本");
		version.setFont(font);
		readme = new JMenuItem("使用说明");
		readme.setFont(font);

		mainJPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 15, 40));
		mainJPanel.add(start);
		mainJPanel.add(end);
		mainJPanel.add(threadNum);
		mainJPanel.add(submit);

		jMenuBar.add(help);
		help.add(author);
		help.add(contact);
		help.add(version);
		help.add(readme);

		add(mainJPanel);
		setJMenuBar(jMenuBar);
		setVisible(true);
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

		submit.addActionListener(this);
		author.addActionListener(this);
		contact.addActionListener(this);
		version.addActionListener(this);
		readme.addActionListener(this);
	}

	public static void main(String[] args) {
		new MainView();

	}


五 处理点击事件

	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == submit) {
			Date date = new Date();
			Format format = new SimpleDateFormat("HH_mm_ss");
			// 结果存储的文件名
			String fileName = format.format(date) + ".txt";

			String startIP = start.getText();
			String endIP = end.getText();
			long sips = IPTraverse.ipToLong(startIP);
			long eips = IPTraverse.ipToLong(endIP);

			int threadNumber = Integer.valueOf(threadNum.getText());

			// 多线程,线程池
			ExecutorService eService = Executors.newFixedThreadPool(50);
			for (int i = 0; i < threadNumber; i++) {
				MyThread myThread = new MyThread(sips, eips, i, threadNumber,
						fileName);
				eService.execute(myThread);
			}
			eService.shutdown();
			while (true) {
				//判断是否全部线程都已经执行结束了
				if (eService.isTerminated()) {
					JOptionPane.showMessageDialog(this, "全部扫描结束", "提示:",
							JOptionPane.INFORMATION_MESSAGE);
					break;
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}

			}

		} else if (e.getSource() == author) {
			JOptionPane.showMessageDialog(this, "zifangsky", "作者:",
					JOptionPane.INFORMATION_MESSAGE);
		} else if (e.getSource() == contact) {
			JOptionPane.showMessageDialog(this,
					"邮箱:admin@zifangsky.cn\n博客:http://www.zifangsky.cn",
					"联系方式:", JOptionPane.INFORMATION_MESSAGE);
		} else if (e.getSource() == version) {
			JOptionPane.showMessageDialog(this, "v1.0.0", "版本号:",
					JOptionPane.INFORMATION_MESSAGE);
		} else if (e.getSource() == readme) {
			JOptionPane
					.showMessageDialog(
							this,
							"我只做了一个简陋的图像化界面,默认只扫描80和8080端口,从左到右分别填起始ip(比如:192.168.0.1)\n;" +
							"结束ip(比如:192.168.250.250);线程数目(别太大,不然有的结果就漏掉了)\n" +
							"还有就是执行的结果会保存在当前目录下的一个txt文件中",
							"使用说明:", JOptionPane.INFORMATION_MESSAGE);
		}

	}

}

在这里,由于单线程的扫描速度很慢,因此我使用了多线程。同时又为了判断全部线程是否都已经执行完毕,我又将这些线程放在了一个线程池里,通过eService.isTerminated()这个方法来判断任务是否全部执行完毕。

其实,这里还有一个关键点,如何快速的遍历一个IP段之间的每个IP?最开始我使用了笨方法(PS:四层for循环依次判断),后来度娘了一下,找到了一个不错的IP工具类,可以将IP在long和String之间相互转化。因此转化成long时,一层for循环就可以遍历了,需要String类型时再将它转化回去就可以了

IPTraverse.java

package util;

public class IPTraverse {
	/**
	 * 将127.0.0.1形式的IP地址转换成十进制整数
	 * @param strIp
	 * @return 整数
	 * */
	public static long ipToLong(String strIp) {  
        long[] ip = new long[4];  
        // 先找到IP地址字符串中.的位置  
        int position1 = strIp.indexOf(".");  
        int position2 = strIp.indexOf(".", position1 + 1);  
        int position3 = strIp.indexOf(".", position2 + 1);  
        // 将每个.之间的字符串转换成整型  
        ip[0] = Long.parseLong(strIp.substring(0, position1));  
        ip[1] = Long.parseLong(strIp.substring(position1 + 1, position2));  
        ip[2] = Long.parseLong(strIp.substring(position2 + 1, position3));  
        ip[3] = Long.parseLong(strIp.substring(position3 + 1));  
        return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];  
    } 
	
	/**
	 * 将十进制整数形式转换成127.0.0.1形式的ip地址  
	 * @param longIp 整数型IP
	 * @return 字符串型IP
	 * */
    public static String longToIP(long longIp) {  
        StringBuffer sb = new StringBuffer("");  
        // 直接右移24位  
        sb.append(String.valueOf((longIp >>> 24)));  
        sb.append(".");  
        // 将高8位置0,然后右移16位  
        sb.append(String.valueOf((longIp & 0x00FFFFFF) >>> 16));  
        sb.append(".");  
        // 将高16位置0,然后右移8位  
        sb.append(String.valueOf((longIp & 0x0000FFFF) >>> 8));  
        sb.append(".");  
        // 将高24位置0  
        sb.append(String.valueOf((longIp & 0x000000FF)));  
        return sb.toString();  
    }  
}


六 多线程批量扫描

先将IP转化成long型数据,然后根据线程数量将这一连续的IP段均匀分给每个线程执行,再通过调用ServerTypeDemo.java这个核心类发出Get请求,最后是将获取到的信息写入到文件中

(PS:关于多线程处理IP段的原理不太理解的可以看我写的这篇文章:http://www.zifangsky.cn/2015/12/多线程循环批量处理以及多线程操作文件写入相关/

MyThread.java:

package action;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import util.IPTraverse;

public class MyThread implements Runnable {
	private long sips; // 起始IP转化的数组
	private long eips; // 结束IP转化的数组
	private int i; // 第几个线程
	private int threadNum; // 总共创建了几个线程
	private String fileName;

	/**
	 * 根据输入的数据多线程批量扫描一个IP段的服务器类型(Apache,tomcat,nginx,IIS。。。)
	 * 
	 * @param sips
	 *            起始IP转化的整数
	 * @param eips
	 *            结束IP转化的整数
	 * @param i
	 *            这是第几个线程
	 * @param fileName
	 *            结果所保存的文件名
	 * @param threadNumber
	 *            扫描的线程数
	 * 
	 * @return null
	 * */
	public MyThread(long sips, long eips, int i, int threadNum, String fileName) {
		this.sips = sips;
		this.eips = eips;
		this.i = i;
		this.threadNum = threadNum;
		this.fileName = fileName;
	}

	public void run() {
		try {
			BufferedWriter writer = new BufferedWriter(new FileWriter(new File(fileName),true));
			// 遍历每个IP
			for (long step = sips + i; step <= eips; step = step + threadNum) {
				String tempIP = IPTraverse.longToIP(step);
//				System.out.println(tempIP);
				//这里只扫描了80和8080端口
				ServerTypeDemo.savaData(tempIP, "80", writer);
				ServerTypeDemo.savaData(tempIP, "8080", writer);
			}
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		

	}
}


七 测试

我随便找了一个IP段进行测试,结果如下:

技术分享

好了,文章到此结束。

(PS:欢迎大家访问我的个人博客网站:http://www.zifangsky.cn

附:完整源代码以及打包好的jar包下载:链接: http://down.51cto.com/data/2125949

(PS:如果没有下载豆的话,可以浏览我个人博客网站上的这篇文章,文末有百度云盘的链接,传送门:http://www.zifangsky.cn/2015/12/多线程批量探测目标ip段的服务器类型/  )

多线程批量探测目标IP段的服务器类型(内网也可用)

标签:java   线程池   服务器类型   内网   

原文地址:http://983836259.blog.51cto.com/7311475/1722429

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!