一 原了解釋
這裡所說的伺服器類型是指像Apache,tomcat,nginx,IIS這種。其中原理用到了HTTP Header的Responses,這裡面有項叫“Server”的參數就包涵我們所需要的資訊。下面是Responses的部分截圖:
<a href="http://s3.51cto.com/wyfs02/M02/77/B7/wKiom1ZszVDikU_EAABUcdoWcOw314.png" target="_blank"></a>
(PS:更多相關可自行百度“HTTP Header”)
是以,我們想要做一個多線程批量探測的軟體,思路有兩種:(1)根據别人提供的接口然後我們去調用擷取(比如:http://api.builtwith.com 這個我以後可能會寫);(2)針對每個IP我們發送Get請求,然後去擷取響應頭檔案中的Server參數
PS:文末我會放出打包好的有GUI界面的jar檔案以及完整源代碼
二 項目結構
這裡我選擇了第二種方式,自己動手做一個,雖然擷取到的資訊沒有用接口擷取的來的全。下面是整個完整小項目的目錄結構:
<a href="http://s4.51cto.com/wyfs02/M00/77/B6/wKioL1ZszYvSZ9IAAAAhxUJ3qCI708.png" target="_blank"></a>
三 核心代碼
在這裡核心代碼在ServerTypeDemo.java這個檔案中,主要是通過對指定IP以及端口發出Get請求,然後擷取響應包中的“Server”,最後将結果寫入檔案。代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<code>package</code> <code>action;</code>
<code>import</code> <code>java.io.BufferedWriter;</code>
<code>import</code> <code>java.io.IOException;</code>
<code>import</code> <code>java.net.HttpURLConnection;</code>
<code>import</code> <code>java.net.MalformedURLException;</code>
<code>import</code> <code>java.net.URL;</code>
<code>import</code> <code>java.util.List;</code>
<code>import</code> <code>java.util.Map;</code>
<code>public</code> <code>class</code> <code>ServerTypeDemo{ </code>
<code> </code><code>/**</code>
<code> </code><code>* 擷取到的伺服器類型寫入txt</code>
<code> </code><code>* @param ip IP</code>
<code> </code><code>* @param port 送出端口</code>
<code> </code><code>* @param writer 寫入流</code>
<code> </code><code>* </code>
<code> </code><code>* @return null</code>
<code> </code><code>* */</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>savaData(String ip,String port,BufferedWriter writer){</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>URL url = </code><code>new</code> <code>URL(</code><code>"http://"</code> <code>+ ip + </code><code>":"</code> <code>+ port);</code>
<code> </code><code>HttpURLConnection connection = (HttpURLConnection) url.openConnection();</code>
<code> </code><code>connection.setRequestMethod(</code><code>"GET"</code><code>);</code>
<code> </code><code>connection.setConnectTimeout(</code><code>1000</code><code>); </code><code>//毫秒</code>
<code> </code><code>connection.setReadTimeout(</code><code>1000</code><code>);</code>
<code> </code><code>Map<String, List<String>> map = connection.getHeaderFields(); </code><code>//擷取HTTP Header Responses</code>
<code> </code>
<code> </code><code>List<String> server = map.get(</code><code>"Server"</code><code>); </code><code>//關鍵點</code>
<code> </code><code>if</code><code>(server == </code><code>null</code><code>){</code>
<code> </code><code>return</code><code>;</code>
<code> </code><code>}</code>
<code> </code><code>else</code><code>{ </code>
<code> </code><code>//寫入檔案</code>
<code> </code><code>for</code><code>(String tmp : server){</code>
<code> </code><code>writer.write(ip + </code><code>":"</code> <code>+ port + </code><code>" "</code> <code>+ tmp);</code>
<code> </code><code>writer.newLine(); </code>
<code> </code><code>}</code>
<code> </code><code>writer.flush();</code>
<code> </code><code>connection.disconnect();</code>
<code> </code>
<code> </code><code>} </code><code>catch</code> <code>(MalformedURLException e) { </code>
<code> </code><code>e.printStackTrace();</code>
<code> </code><code>} </code><code>catch</code> <code>(IOException e) { </code>
<code> </code><code>}</code>
<code> </code><code>}</code>
<code> </code>
<code>}</code>
四 一個簡陋界面
<a href="http://s3.51cto.com/wyfs02/M00/77/B7/wKiom1ZszdSxEenNAAB0fFaCOSw024.png" target="_blank"></a>
從左到右分别填:起始IP,結束IP(PS:在這裡兩個IP不在一個C段也行,比如:192.168.1.1~192.168.255.255),線程數,最後點選開始進行掃描,待全部線程結束後會給出提示資訊。界面相關代碼如下:
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<code>package</code> <code>view;</code>
<code>import</code> <code>java.awt.Dimension;</code>
<code>import</code> <code>java.awt.FlowLayout;</code>
<code>import</code> <code>java.awt.Font;</code>
<code>import</code> <code>java.awt.Toolkit;</code>
<code>import</code> <code>java.awt.event.ActionEvent;</code>
<code>import</code> <code>java.awt.event.ActionListener;</code>
<code>import</code> <code>java.text.Format;</code>
<code>import</code> <code>java.text.SimpleDateFormat;</code>
<code>import</code> <code>java.util.Date;</code>
<code>import</code> <code>java.util.concurrent.ExecutorService;</code>
<code>import</code> <code>java.util.concurrent.Executors;</code>
<code>import</code> <code>javax.swing.JButton;</code>
<code>import</code> <code>javax.swing.JFrame;</code>
<code>import</code> <code>javax.swing.JMenu;</code>
<code>import</code> <code>javax.swing.JMenuBar;</code>
<code>import</code> <code>javax.swing.JMenuItem;</code>
<code>import</code> <code>javax.swing.JOptionPane;</code>
<code>import</code> <code>javax.swing.JPanel;</code>
<code>import</code> <code>javax.swing.JTextField;</code>
<code>import</code> <code>util.IPTraverse;</code>
<code>import</code> <code>action.MyThread;</code>
<code>public</code> <code>class</code> <code>MainView </code><code>extends</code> <code>JFrame </code><code>implements</code> <code>ActionListener {</code>
<code> </code><code>* 此程式是為了批量探測目标IP段的伺服器類型(Apache,tomcat,nginx,IIS。。。) 其中用到了線程池,可以自定義掃描線程數量</code>
<code> </code><code>* (PS:隻做了一個簡陋的界面 O(∩_∩)O~)</code>
<code> </code><code>* @author zifangsky</code>
<code> </code><code>* @blog http://www.zifangsky.cn</code>
<code> </code><code>* @version V1.0.0</code>
<code> </code><code>* @date 2015-12-9</code>
<code> </code><code>private</code> <code>static</code> <code>final</code> <code>long</code> <code>serialVersionUID = 1L;</code>
<code> </code><code>private</code> <code>JPanel mainJPanel;</code>
<code> </code><code>private</code> <code>JTextField start, end, threadNum; </code><code>// 起始IP,結束IP,線程值</code>
<code> </code><code>private</code> <code>JButton submit;</code>
<code> </code><code>private</code> <code>JMenuBar jMenuBar;</code>
<code> </code><code>private</code> <code>JMenu help; </code><code>// 幫助</code>
<code> </code><code>private</code> <code>JMenuItem author, contact, version, readme; </code><code>// 作者,郵箱,版本号,使用說明</code>
<code> </code><code>private</code> <code>Font font = </code><code>new</code> <code>Font(</code><code>"宋體"</code><code>, Font.LAYOUT_NO_LIMIT_CONTEXT, </code><code>16</code><code>);</code>
<code> </code><code>public</code> <code>MainView() {</code>
<code> </code><code>super</code><code>(</code><code>"批量判斷伺服器類型(Apache,tomcat,nginx。。。) by zifangsky"</code><code>);</code>
<code> </code><code>Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();</code>
<code> </code><code>screenSize = Toolkit.getDefaultToolkit().getScreenSize(); </code><code>// 螢幕大小</code>
<code> </code><code>setPreferredSize(</code><code>new</code> <code>Dimension(</code><code>600</code><code>, </code><code>300</code><code>));</code>
<code> </code><code>int</code> <code>frameWidth = </code><code>this</code><code>.getPreferredSize().width; </code><code>// 界面寬度</code>
<code> </code><code>int</code> <code>frameHeight = </code><code>this</code><code>.getPreferredSize().height; </code><code>// 界面高度</code>
<code> </code><code>setSize(frameWidth, frameHeight);</code>
<code> </code><code>setLocation((screenSize.width - frameWidth) / </code><code>2</code><code>,</code>
<code> </code><code>(screenSize.height - frameHeight) / </code><code>2</code><code>);</code>
<code> </code><code>mainJPanel = </code><code>new</code> <code>JPanel();</code>
<code> </code><code>start = </code><code>new</code> <code>JTextField(</code><code>"192.168.1.1"</code><code>, </code><code>12</code><code>);</code>
<code> </code><code>end = </code><code>new</code> <code>JTextField(</code><code>"192.168.1.254"</code><code>, </code><code>12</code><code>);</code>
<code> </code><code>threadNum = </code><code>new</code> <code>JTextField(</code><code>"5"</code><code>, </code><code>8</code><code>);</code>
<code> </code><code>submit = </code><code>new</code> <code>JButton(</code><code>"開始"</code><code>);</code>
<code> </code><code>submit.setFont(font);</code>
<code> </code><code>jMenuBar = </code><code>new</code> <code>JMenuBar();</code>
<code> </code><code>help = </code><code>new</code> <code>JMenu(</code><code>"幫助"</code><code>);</code>
<code> </code><code>help.setFont(font);</code>
<code> </code><code>author = </code><code>new</code> <code>JMenuItem(</code><code>"作者"</code><code>);</code>
<code> </code><code>author.setFont(font);</code>
<code> </code><code>contact = </code><code>new</code> <code>JMenuItem(</code><code>"聯系方式"</code><code>);</code>
<code> </code><code>contact.setFont(font);</code>
<code> </code><code>version = </code><code>new</code> <code>JMenuItem(</code><code>"版本"</code><code>);</code>
<code> </code><code>version.setFont(font);</code>
<code> </code><code>readme = </code><code>new</code> <code>JMenuItem(</code><code>"使用說明"</code><code>);</code>
<code> </code><code>readme.setFont(font);</code>
<code> </code><code>mainJPanel.setLayout(</code><code>new</code> <code>FlowLayout(FlowLayout.CENTER, </code><code>15</code><code>, </code><code>40</code><code>));</code>
<code> </code><code>mainJPanel.add(start);</code>
<code> </code><code>mainJPanel.add(end);</code>
<code> </code><code>mainJPanel.add(threadNum);</code>
<code> </code><code>mainJPanel.add(submit);</code>
<code> </code><code>jMenuBar.add(help);</code>
<code> </code><code>help.add(author);</code>
<code> </code><code>help.add(contact);</code>
<code> </code><code>help.add(version);</code>
<code> </code><code>help.add(readme);</code>
<code> </code><code>add(mainJPanel);</code>
<code> </code><code>setJMenuBar(jMenuBar);</code>
<code> </code><code>setVisible(</code><code>true</code><code>);</code>
<code> </code><code>setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);</code>
<code> </code><code>submit.addActionListener(</code><code>this</code><code>);</code>
<code> </code><code>author.addActionListener(</code><code>this</code><code>);</code>
<code> </code><code>contact.addActionListener(</code><code>this</code><code>);</code>
<code> </code><code>version.addActionListener(</code><code>this</code><code>);</code>
<code> </code><code>readme.addActionListener(</code><code>this</code><code>);</code>
<code> </code><code>public</code> <code>static</code> <code>void</code> <code>main(String[] args) {</code>
<code> </code><code>new</code> <code>MainView();</code>
五 處理點選事件
<code> </code><code>public</code> <code>void</code> <code>actionPerformed(ActionEvent e) {</code>
<code> </code><code>if</code> <code>(e.getSource() == submit) {</code>
<code> </code><code>Date date = </code><code>new</code> <code>Date();</code>
<code> </code><code>Format format = </code><code>new</code> <code>SimpleDateFormat(</code><code>"HH_mm_ss"</code><code>);</code>
<code> </code><code>// 結果存儲的檔案名</code>
<code> </code><code>String fileName = format.format(date) + </code><code>".txt"</code><code>;</code>
<code> </code><code>String startIP = start.getText();</code>
<code> </code><code>String endIP = end.getText();</code>
<code> </code><code>long</code> <code>sips = IPTraverse.ipToLong(startIP);</code>
<code> </code><code>long</code> <code>eips = IPTraverse.ipToLong(endIP);</code>
<code> </code><code>int</code> <code>threadNumber = Integer.valueOf(threadNum.getText());</code>
<code> </code><code>// 多線程,線程池</code>
<code> </code><code>ExecutorService eService = Executors.newFixedThreadPool(</code><code>50</code><code>);</code>
<code> </code><code>for</code> <code>(</code><code>int</code> <code>i = </code><code>0</code><code>; i < threadNumber; i++) {</code>
<code> </code><code>MyThread myThread = </code><code>new</code> <code>MyThread(sips, eips, i, threadNumber,</code>
<code> </code><code>fileName);</code>
<code> </code><code>eService.execute(myThread);</code>
<code> </code><code>eService.shutdown();</code>
<code> </code><code>while</code> <code>(</code><code>true</code><code>) {</code>
<code> </code><code>//判斷是否全部線程都已經執行結束了</code>
<code> </code><code>if</code> <code>(eService.isTerminated()) {</code>
<code> </code><code>JOptionPane.showMessageDialog(</code><code>this</code><code>, </code><code>"全部掃描結束"</code><code>, </code><code>"提示:"</code><code>,</code>
<code> </code><code>JOptionPane.INFORMATION_MESSAGE);</code>
<code> </code><code>break</code><code>;</code>
<code> </code><code>try</code> <code>{</code>
<code> </code><code>Thread.sleep(</code><code>1000</code><code>);</code>
<code> </code><code>} </code><code>catch</code> <code>(InterruptedException e1) {</code>
<code> </code><code>e1.printStackTrace();</code>
<code> </code><code>} </code><code>else</code> <code>if</code> <code>(e.getSource() == author) {</code>
<code> </code><code>JOptionPane.showMessageDialog(</code><code>this</code><code>, </code><code>"zifangsky"</code><code>, </code><code>"作者:"</code><code>,</code>
<code> </code><code>JOptionPane.INFORMATION_MESSAGE);</code>
<code> </code><code>} </code><code>else</code> <code>if</code> <code>(e.getSource() == contact) {</code>
<code> </code><code>JOptionPane.showMessageDialog(</code><code>this</code><code>,</code>
<code> </code><code>"郵箱:[email protected]\n部落格:http://www.zifangsky.cn"</code><code>,</code>
<code> </code><code>"聯系方式:"</code><code>, JOptionPane.INFORMATION_MESSAGE);</code>
<code> </code><code>} </code><code>else</code> <code>if</code> <code>(e.getSource() == version) {</code>
<code> </code><code>JOptionPane.showMessageDialog(</code><code>this</code><code>, </code><code>"v1.0.0"</code><code>, </code><code>"版本号:"</code><code>,</code>
<code> </code><code>} </code><code>else</code> <code>if</code> <code>(e.getSource() == readme) {</code>
<code> </code><code>JOptionPane</code>
<code> </code><code>.showMessageDialog(</code>
<code> </code><code>this</code><code>,</code>
<code> </code><code>"我隻做了一個簡陋的圖像化界面,預設隻掃描80和8080端口,從左到右分别填起始ip(比如:192.168.0.1)\n;"</code> <code>+</code>
<code> </code><code>"結束ip(比如:192.168.250.250);線程數目(别太大,不然有的結果就漏掉了)\n"</code> <code>+</code>
<code> </code><code>"還有就是執行的結果會儲存在目前目錄下的一個txt檔案中"</code><code>,</code>
<code> </code><code>"使用說明:"</code><code>, JOptionPane.INFORMATION_MESSAGE);</code>
在這裡,由于單線程的掃描速度很慢,是以我使用了多線程。同時又為了判斷全部線程是否都已經執行完畢,我又将這些線程放在了一個線程池裡,通過eService.isTerminated()這個方法來判斷任務是否全部執行完畢。
其實,這裡還有一個關鍵點,如何快速的周遊一個IP段之間的每個IP?最開始我使用了笨方法(PS:四層for循環依次判斷),後來度娘了一下,找到了一個不錯的IP工具類,可以将IP在long和String之間互相轉化。是以轉化成long時,一層for循環就可以周遊了,需要String類型時再将它轉化回去就可以了
IPTraverse.java:
<code>package</code> <code>util;</code>
<code>public</code> <code>class</code> <code>IPTraverse {</code>
<code> </code><code>* 将127.0.0.1形式的IP位址轉換成十進制整數</code>
<code> </code><code>* @param strIp</code>
<code> </code><code>* @return 整數</code>
<code> </code><code>public</code> <code>static</code> <code>long</code> <code>ipToLong(String strIp) { </code>
<code> </code><code>long</code><code>[] ip = </code><code>new</code> <code>long</code><code>[</code><code>4</code><code>]; </code>
<code> </code><code>// 先找到IP位址字元串中.的位置 </code>
<code> </code><code>int</code> <code>position1 = strIp.indexOf(</code><code>"."</code><code>); </code>
<code> </code><code>int</code> <code>position2 = strIp.indexOf(</code><code>"."</code><code>, position1 + </code><code>1</code><code>); </code>
<code> </code><code>int</code> <code>position3 = strIp.indexOf(</code><code>"."</code><code>, position2 + </code><code>1</code><code>); </code>
<code> </code><code>// 将每個.之間的字元串轉換成整型 </code>
<code> </code><code>ip[</code><code>0</code><code>] = Long.parseLong(strIp.substring(</code><code>0</code><code>, position1)); </code>
<code> </code><code>ip[</code><code>1</code><code>] = Long.parseLong(strIp.substring(position1 + </code><code>1</code><code>, position2)); </code>
<code> </code><code>ip[</code><code>2</code><code>] = Long.parseLong(strIp.substring(position2 + </code><code>1</code><code>, position3)); </code>
<code> </code><code>ip[</code><code>3</code><code>] = Long.parseLong(strIp.substring(position3 + </code><code>1</code><code>)); </code>
<code> </code><code>return</code> <code>(ip[</code><code>0</code><code>] << </code><code>24</code><code>) + (ip[</code><code>1</code><code>] << </code><code>16</code><code>) + (ip[</code><code>2</code><code>] << </code><code>8</code><code>) + ip[</code><code>3</code><code>]; </code>
<code> </code><code>} </code>
<code> </code><code>* 将十進制整數形式轉換成127.0.0.1形式的ip位址 </code>
<code> </code><code>* @param longIp 整數型IP</code>
<code> </code><code>* @return 字元串型IP</code>
<code> </code><code>public</code> <code>static</code> <code>String longToIP(</code><code>long</code> <code>longIp) { </code>
<code> </code><code>StringBuffer sb = </code><code>new</code> <code>StringBuffer(</code><code>""</code><code>); </code>
<code> </code><code>// 直接右移24位 </code>
<code> </code><code>sb.append(String.valueOf((longIp >>> </code><code>24</code><code>))); </code>
<code> </code><code>sb.append(</code><code>"."</code><code>); </code>
<code> </code><code>// 将高8位置0,然後右移16位 </code>
<code> </code><code>sb.append(String.valueOf((longIp & </code><code>0x00FFFFFF</code><code>) >>> </code><code>16</code><code>)); </code>
<code> </code><code>// 将高16位置0,然後右移8位 </code>
<code> </code><code>sb.append(String.valueOf((longIp & </code><code>0x0000FFFF</code><code>) >>> </code><code>8</code><code>)); </code>
<code> </code><code>// 将高24位置0 </code>
<code> </code><code>sb.append(String.valueOf((longIp & </code><code>0x000000FF</code><code>))); </code>
<code> </code><code>return</code> <code>sb.toString(); </code>
<code> </code><code>} </code>
六 多線程批量掃描
先将IP轉化成long型資料,然後根據線程數量将這一連續的IP段均勻分給每個線程執行,再通過調用ServerTypeDemo.java這個核心類發出Get請求,最後是将擷取到的資訊寫入到檔案中
(PS:關于多線程處理IP段的原理不太了解的可以看我寫的這篇文章:http://www.zifangsky.cn/2015/12/多線程循環批量處理以及多線程操作檔案寫入相關/)
MyThread.java:
<code>import</code> <code>java.io.File;</code>
<code>import</code> <code>java.io.FileWriter;</code>
<code>public</code> <code>class</code> <code>MyThread </code><code>implements</code> <code>Runnable {</code>
<code> </code><code>private</code> <code>long</code> <code>sips; </code><code>// 起始IP轉化的數組</code>
<code> </code><code>private</code> <code>long</code> <code>eips; </code><code>// 結束IP轉化的數組</code>
<code> </code><code>private</code> <code>int</code> <code>i; </code><code>// 第幾個線程</code>
<code> </code><code>private</code> <code>int</code> <code>threadNum; </code><code>// 總共建立了幾個線程</code>
<code> </code><code>private</code> <code>String fileName;</code>
<code> </code><code>* 根據輸入的資料多線程批量掃描一個IP段的伺服器類型(Apache,tomcat,nginx,IIS。。。)</code>
<code> </code><code>* @param sips</code>
<code> </code><code>* 起始IP轉化的整數</code>
<code> </code><code>* @param eips</code>
<code> </code><code>* 結束IP轉化的整數</code>
<code> </code><code>* @param i</code>
<code> </code><code>* 這是第幾個線程</code>
<code> </code><code>* @param fileName</code>
<code> </code><code>* 結果所儲存的檔案名</code>
<code> </code><code>* @param threadNumber</code>
<code> </code><code>* 掃描的線程數</code>
<code> </code><code>public</code> <code>MyThread(</code><code>long</code> <code>sips, </code><code>long</code> <code>eips, </code><code>int</code> <code>i, </code><code>int</code> <code>threadNum, String fileName) {</code>
<code> </code><code>this</code><code>.sips = sips;</code>
<code> </code><code>this</code><code>.eips = eips;</code>
<code> </code><code>this</code><code>.i = i;</code>
<code> </code><code>this</code><code>.threadNum = threadNum;</code>
<code> </code><code>this</code><code>.fileName = fileName;</code>
<code> </code><code>public</code> <code>void</code> <code>run() {</code>
<code> </code><code>BufferedWriter writer = </code><code>new</code> <code>BufferedWriter(</code><code>new</code> <code>FileWriter(</code><code>new</code> <code>File(fileName),</code><code>true</code><code>));</code>
<code> </code><code>// 周遊每個IP</code>
<code> </code><code>for</code> <code>(</code><code>long</code> <code>step = sips + i; step <= eips; step = step + threadNum) {</code>
<code> </code><code>String tempIP = IPTraverse.longToIP(step);</code>
<code>// System.out.println(tempIP);</code>
<code> </code><code>//這裡隻掃描了80和8080端口</code>
<code> </code><code>ServerTypeDemo.savaData(tempIP, </code><code>"80"</code><code>, writer);</code>
<code> </code><code>ServerTypeDemo.savaData(tempIP, </code><code>"8080"</code><code>, writer);</code>
<code> </code><code>writer.close();</code>
<code> </code><code>} </code><code>catch</code> <code>(IOException e) {</code>
七 測試
我随便找了一個IP段進行測試,結果如下:
<a href="http://s3.51cto.com/wyfs02/M01/77/B7/wKiom1ZszmWyrrjrAAEoAbwgg1E026.png" target="_blank"></a>
好了,文章到此結束。
本文轉自 pangfc 51CTO部落格,原文連結:http://blog.51cto.com/983836259/1722429,如需轉載請自行聯系原作者