如果每个65536端口需要200ms(在最坏的情况下,防火墙会阻塞所有的内容,这样您就可以为每一个端口启用超时),数学很简单:您需要13k秒或大约3个小时一半。
您有2(非排他)选项可以使其更快:
>减少你的超时
> paralellize你的代码
由于操作是I / O绑定(与CPU绑定相反 – 也就是说,您花费时间等待I / O,而不是为了完成一些巨大的计算),您可以使用许多线程。尝试从20开始。他们会分开3个半小时,所以最大预期时间约为10分钟。只要记住,这将对另一方施加压力,即扫描的主机将看到巨大的网络活动与“不合理”或“奇怪”的模式,使扫描非常容易检测。
最简单的方法(即最小化的变化)是使用ExecutorService和Future API:
public static Future portIsOpen(final ExecutorService es, final String ip, final int port, final int timeout) {
return es.submit(new Callable() {
@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> 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 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;
// constructor
// getters
}
然后,在第一个代码片段中将布尔值更改为ScanResult,并返回新的ScanResult(port,true)或新的ScanResult(port,false),而不是true或false
编辑:其实我刚刚注意到:在这种特殊情况下,您不需要ScanResult类来保存结果端口,并且仍然知道哪个端口是打开的。由于您将期货添加到已订购的列表中,并且稍后您将以与您添加的列表相同的顺序处理它们,您可以在每个迭代中增加一个计数器,以了解您正在处理哪个端口。但是,嘿,这只是要完整和准确。不要尝试这样做,这是可怕的,我很ash愧,我想到了这一点…使用ScanResult对象更干净,代码更容易阅读和维护,并允许您,稍后,例如,使用CompletionService来改进扫描仪。