如果每个65536个端口需要200毫秒(在最坏的情况下,防火墙阻止所有连接,因此每个端口都会超时),那么数学计算就很简单:您需要13,000秒,大约3个半小时。
您有两个(非排他性)选项可以使它更快:
由于该操作是I/O绑定的(相对于CPU绑定--也就是说,您花费等待I/O而不是一些巨大计算完成的时间),您可以使用许多,许多线程。尝试从20开始。他们将在其中分配3个半小时的时间,因此最长预期时间约为10分钟。只需记住,这将对另一侧产生压力,即,扫描的主机将看到巨大的网络活动,并显示“不合理”或“奇怪”的模式,从而使扫描极易被检测到。
最简单的方法(即,具有最小更改)是使用ExecutorService和Future API:
public static Future<Boolean> portIsOpen(final ExecutorService es, final String ip, final int port, final int timeout) {
return es.submit(new Callable<Boolean>() {
@Override public Boolean call() {
try {
Socket socket = new Socket();
socket.connect(new InetSocketAddress(ip, port), timeout);
socket.close();
return true;
} catch (Exception ex) {
return false;
}
}
});
}
然后,你可以做类似以下的操作:
public static void main(final String... args) {
final ExecutorService es = Executors.newFixedThreadPool(20);
final String ip = "127.0.0.1";
final int timeout = 200;
final List<Future<Boolean>> futures = new ArrayList<>();
for (int port = 1; port <= 65535; port++) {
futures.add(portIsOpen(es, ip, port, timeout));
}
es.shutdown();
int openPorts = 0;
for (final Future<Boolean> f : futures) {
if (f.get()) {
openPorts++;
}
}
System.out.println("There are " + openPorts + " open ports on host " + ip + " (probed with a timeout of " + timeout + "ms)");
}
如果您需要知道开放了哪些端口(而不只是像上面的示例中所示的那样知道有多少个端口是开放的),您需要将函数的返回类型更改为Future<SomethingElse>
,其中SomethingElse
将保存端口和扫描结果,例如:
public final class ScanResult {
private final int port;
private final boolean isOpen;
}
然后,在第一段代码中将
Boolean
更改为
ScanResult
,并返回
new ScanResult(port, true)
或
new ScanResult(port, false)
而不是仅返回
true
或
false
。编辑:实际上,我刚刚注意到:在这种特定情况下,您不需要使用ScanResult类来保存结果+端口,并且仍然知道哪个端口是开放的。由于您将未来添加到
List 中,它是有序的,并且稍后,您会按照添加顺序进行处理,因此可以有一个计数器,您将在每次迭代时递增,以了解正在处理哪个端口。但是,嘿,这只是为了完整和精确。永远不要尝试这样做,这太可怕了,我大多数时间都感到惭愧... 使用ScanResult对象要干净得多,代码易于阅读和维护,并允许您稍后例如使用CompletionService
来改进扫描器。