首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程網段掃描器:高效 Ping 掃描實作

     編輯:Java 相關知識

專案目標與效能指標

  • 掃描 500 IP 耗時 ~10 秒
  • 單 IP 等待上限 0.3 秒
  • 線程池控制:最大 30 條並發線程
  • 結果儲存於 Hashtable(IP → Boolean 可達性)

核心架構設計

生產者-消費者模式

public class IpScanner implements Runnable {
    // 靜態共享資源
    static Hashtable pingResults = new Hashtable();
    static int activeThreads = 0;
    static final int MAX_THREADS = 30;
    
    // 掃描目標 IP
    private String targetIp;
}

線程啟動控制 Ping(String ip)

public static void Ping(String ip) throws Exception {
    // 等待可用線程槽
    while (activeThreads > MAX_THREADS) 
        Thread.sleep(50);
    
    activeThreads++;
    new Thread(new IpScanner(ip)).start();
}

掃描執行邏輯 run()

public void run() {
    try {
        // 執行系統 ping 命令:Windows 參數 -w 300ms -n 1
        Process p = Runtime.getRuntime().exec("ping " + targetIp + " -w 300 -n 1");
        InputStreamReader ir = new InputStreamReader(p.getInputStream());
        LineNumberReader input = new LineNumberReader(ir);
        
        // 跳過前 6 行標題輸出
        for (int i = 1; i < 7; i++) input.readLine();
        
        // 第 7 行包含回應資訊
        String line = input.readLine();
        boolean reachable = !(line.length() < 17 || line.substring(8, 17).equals("timed out"));
        
        pingResults.put(targetIp, reachable);
    } catch (IOException e) {
        pingResults.put(targetIp, false);
    } finally {
        activeThreads--;  // 釋放線程槽
    }
}

批次掃描協調 PingAll()

public void PingAll() throws Exception {
    activeThreads = 0;
    pingResults = new Hashtable();
    
    // 遍歷所有目標 IP(需實作 next()/cur.ip 邏輯)
    while (next()) {
        Ping(cur.ip);
    }
    
    // 等待所有線程完成
    while (activeThreads > 0) 
        Thread.sleep(50);
}

關鍵技術細節

平台相容性處理

原始碼使用 Windows ping 參數(-w 超時毫秒、-n 封包數)。Linux/macOS 需改為 -W 1 -c 1。建議封裝平台差異:

String pingCmd = System.getProperty("os.name").startsWith("Windows") 
    ? "ping -w 300 -n 1 " 
    : "ping -W 1 -c 1 ";

資源洩漏風險

  • Process 未銷毀:需呼叫 p.destroy() 或確保 p.waitFor()
  • 流未關閉:使用 try-with-resources 管理 InputStreamReader
  • 線程計數競態:activeThreads++/-- 應用 AtomicInteger 或 synchronized

進階優化方向

1. 使用 Java 原生 InetAddress.isReachable()

boolean reachable = InetAddress.getByName(ip).isReachable(300);

優點:跨平台、無需解析文字輸出。缺點:需 ICMP 權限(Linux 需 root),某些防火牆封鎖。

2. NIO 非阻塞 Socket 掃描

針對 TCP 端口掃描(如 80, 443, 3389),使用 Selector 實現萬級並發,效能遠超進程啟動。

3. 線程池替換手動計數

ExecutorService pool = Executors.newFixedThreadPool(30);
for (String ip : targets) {
    pool.submit(() -> scanIp(ip));
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.MINUTES);

4. 結果持久化與擴充

  • 整合 nbtstat -A 獲取 NetBIOS 名稱、MAC、登入用戶
  • 結果寫入資料庫/CSV,支援歷史比對
  • Web 介面即時顯示掃描進度與熱力圖

完整可執行範例(修正版)

import java.io.*;
import java.util.*;
import java.util.concurrent.*;

public class NetworkScanner {
    static ConcurrentMap results = new ConcurrentHashMap<>();
    static final int MAX_THREADS = 50;
    
    public static void main(String[] args) throws Exception {
        String baseIp = "192.168.1.";
        ExecutorService pool = Executors.newFixedThreadPool(MAX_THREADS);
        
        for (int i = 1; i <= 254; i++) {
            final String ip = baseIp + i;
            pool.submit(() -> {
                try {
                    boolean up = InetAddress.getByName(ip).isReachable(200);
                    results.put(ip, up);
                    if (up) System.out.println(ip + " 可達");
                } catch (IOException e) {
                    results.put(ip, false);
                }
            });
        }
        
        pool.shutdown();
        pool.awaitTermination(2, TimeUnit.MINUTES);
        System.out.println("掃描完成,共 " + results.size() + " 個 IP");
    }
}

總結

多線程 Ping 掃描是網路管理基礎工具。原始碼展示了手動線程控制的基本邏輯,但生產環境建議採用線程池、原生 API、跨平台命令適配與資源安全管理。進階需求可延伸為完整資產發現系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved