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

一 原理解释

这里所说的服务器类型是指像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,
					"邮箱:[email protected]\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段的服务器类型/  )

时间: 2024-12-15 01:39:19

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

centos 单网卡批量添加不同IP段

单网卡批量添加不同IP段70.39.112.194 添加IP:70.39.112.195-222/70.39.83.226-254 vi /etc/sysconfig/network-scripts/ifcfg-eth0-range0DEVICE=eth0ONBOOT=yesBOOTPROTO=staticIPADDR_START=70.39.112.195IPADDR_END=70.39.112.222CLONENUM_START=0GATEWAY=70.39.112.193NETMASK=2

一键批量ping任意ip段的存活主机

一键批量ping任意ip段的存活主机 今晚继续我们的电脑批处理系列教程.如果想知道自己的小区宽带或局域网,甚至外网任意某一ip段,目前有多少人同时在上网肿么办呢?可能大家想到的一定是找工具什么的.其实不然,利用Windows内置的批处理功能完全可以实现批量ping查找指定网段存活主机的目的! @echo offcolor etitle 批处理扫网段主机echo.@for /f "tokens=1-4 delims=." %%i in (ip.txt) do (@for /l %%n i

利用云服务器搭建内网映射服务器

应用背景:众所周知,随着公网IP地址的短缺,即使电信联通这样的一级运营商在提供宽带服务的时候有时候也会使用私网IP地址,更不用说长城.鹏博士这样的二级运营商了,肯定都是提供的私网IP地址,那么对于一个计算机发烧友来讲,经常会使用很多服务,但是这些服务往往都是搭建在自己本地的电脑上的,可是为了让公网能够测试访问,就必须使用端口映射技术,如果有幸获取到公网IP则可以不必继续读下去,如果为了省钱,购买了二级运营商的宽带,抑或是教育网的同学们想利用自己的校园网搭建一些公网服务进行测试或者学习怎么办呢?那

配置树莓派3和局域网NTP服务器实现内网时间校准

一.配置局域网NTP服务器 1.安装ntp-4.2.8p5-win32-setup.exe 下载地址:https://www.meinbergglobal.com/english/sw/ntp.htm按默认步骤安装即可. 2.修改配置文件ntp.conf 配置文件默认路径为:C:\Program Files (x86)\NTP\etc\ntp.conf去掉 [plain] view plain copy #server 127.127.1.0 #fudge 127.127.1.0 stratum

linux外网服务器跳转内网服务器实现内网访问(iptables)

服务器信息如下 外网服务器:eth0:公网IP eth1:内网IP(192.168.10.205) 公网对外提供服务,内网IP与内部局域网通信. 配置过程: (1)开启iptables service iptables restart   (临时开启) chkconfig  iptables on    (永久开启) (2)配置iptables          (2-1)配置filter选项,使得IP和PORT可以通过防火墙 注意:在filter选项下配置 例如:#18081(tomcat-O

SVN服务器搭建 内网可用外网不可用的问题

检查1:内网端口映射到了外网端口,这样外网才能够访问到 映射的方式有两种: 1.通过路由器的虚拟服务器功能,网上一搜一大把. 2.将路由器的DMZ功能开启,并把DMZ主机设置为目标计算机. 检查2:SVN服务器的设置 .VisualSVN -> "操作" -> "Properties" ->"Network": Server name:改为自己的局域网ip port:改为常用端口号,一般使用默认的443或者自己设置的都可以(需要

Vue实战041:获取当前客户端IP地址详解(内网和外网)

前言 我们经常会有需求,希望能获取的到当前用户的IP地址,而IP又分为公网ip(也称外网)和私网IP(也称内网IP),IP地址是IP协议提供的一种统一的地址格式,每台设备都设定了一个唯一的IP地址”,从而确保了用户在连网的计算机上操作时,能够快速地从互联网中找到自己所需的对象. 外网IP和内网IP的区别 1,外网IP是全球唯一的IP地址,仅分配给某一台网络设备.内网IP是由路由器分配给每一台设备内部使用的IP地址: 2,外网IP任何一台设备都可以ping通.内网IP只有在同一环境的内部设备才能p

route ubuntu配置IP,通过路由到内网

首先,手动配置好内网的固定IP sudo ifconfig eth0 10.2.98.98 netmask 255.255.255.0 up 然后 // sudo route add -net 10.23.0.0 netmask 255.255.0.0 eth0 sudo route add -net 10.23.0.0 netmask 255.255.0.0 gw 10.2.98.1

阿里云ecs服务器wamp内网可以访问,外网ip,域名无法访问

问题:用的阿里云服务器,windows2008 R2,域名解析正常.本地localhost,127.0.0.1可以正常打开网站,说明80端口没有被占用. 解决办法: 关闭防火墙之后,外部可以正常访问网站. 网站不可能一直关闭windows防火墙,所以判断是80端口尚未开启. 用一下办法打开80端口入站规则,开启防火墙之后可以正常访问.(做一下操作的前提是在阿里云ecs控制台->本实例安全组规则中设置好80端口允许访问,否则一样无法访问) http://jingyan.baidu.com/arti