您可能试图一次打开过多的连接并耗尽资源或在接收服务器处被阻塞以使其泛滥。
因为您所有的套接字内容都是异步的,所以您的 for 循环会立即尝试打开 35,000 个套接字。虽然可以将服务器配置为执行此操作,但它需要非常特殊的配置,并且使用每个套接字需要低内存方案(您在这里没有)。
因此,一个简单的解决方案是将同时打开的套接字数量限制在某个合理的范围内。
这里有一些代码,最初打开固定数量的套接字,然后随着每个套接字完成,它会打开另一个,保持固定数量的打开和活动套接字。当前设置为立即打开100 套接字。您可以在您的特定环境和操作系统中进行试验,看看您可以在不产生问题的情况下使该数字达到多高:
var net = require('net');
var host = 'scanme.nmap.org';
var low = 1;
var high = 35000;
var maxInFlight = 100;
function scanPort(port) {
return new Promise(function(resolve, reject) {
var c = new net.Socket();
c.setTimeout(5000);
c.connect({
port: port,
host: host
})
c.on('connect', function () {
console.log(port);
c.destroy();
resolve(port);
})
c.on('error', function (err) {
c.destroy();
reject(err);
})
c.on('timeout', function () {
c.destroy();
reject({code: "Timeout", port: port});
})
});
}
var cntr = low;
var inFlightCnt = 0;
function run() {
while (inFlightCnt < maxInFlight && cntr <= high) {
++inFlightCnt;
scanPort(cntr++).then(function(port) {
--inFlightCnt;
run();
}, function(err) {
--inFlightCnt;
run();
});
}
}
run();
注意:当一次设置为100 时,这需要一段时间才能通过所有端口(最多 35,000 个)。它确实找到了您提到的四个开放端口。
而且,这是另一个版本,它收集有关每个端口以及它如何失败/成功的信息,以便您在最后得到一个转储。它还在控制台中显示进度,因此您可以查看它是否仍在运行以及距离完成还有多远:
var net = require('net');
var host = 'scanme.nmap.org';
var low = 1;
var high = 35000;
var maxInFlight = 200;
function scanPort(port) {
return new Promise(function(resolve, reject) {
var c = new net.Socket();
c.setTimeout(15000);
c.connect({
port: port,
host: host
})
c.on('connect', function () {
c.destroy();
resolve(port);
})
c.on('error', function (err) {
c.destroy();
reject(err);
})
c.on('timeout', function () {
c.destroy();
reject({code: "timeout", port: port});
})
});
}
var cntr = low;
var inFlightCnt = 0;
var openPorts = [];
var timeouts = [];
var refused = [];
var otherErrors = [];
function run() {
while (inFlightCnt < maxInFlight && cntr <= high) {
++inFlightCnt;
scanPort(cntr++).then(function(port) {
--inFlightCnt;
openPorts.push(port);
console.log(openPorts);
run();
}, function(err) {
if (err.code === "timeout" || err.code === "ETIMEDOUT") {
timeouts.push(err.port);
} else if (err.code === "ECONNREFUSED") {
refused.push(err.port);
} else {
otherErrors.push(err.port);
}
console.log(err.code + ": " + err.port);
--inFlightCnt;
run();
});
}
// if we are all done here, log the open ports
if (inFlightCnt === 0 && cntr >= high) {
console.log("open: " + JSON.stringify(openPorts));
console.log("timeouts: " + JSON.stringify(timeouts));
console.log("otherErrors: " + JSON.stringify(otherErrors));
}
}
run();
这是我在带有节点 v4.0.0 的 Windows 10 上运行它时生成的输出:
open: [22,80,9929,31337]
timeouts: [25,135,136,137,138,139,445,974,984,972,965,963,964,978,985,980,975,981,987,971,960,977,1000,992,990,986,991,997,1391,1384,7455,7459,7450,7506,7512,23033,23736,33635,33640,33638,33641,33634,33636,33633]
otherErrors: []
我不知道为什么有些端口会超时。大多数得到ECONNREFUSED,这是您对未打开端口的期望,少数从net 库中得到ETIMEDOUT,并且从代码中的超时中得到一些超时(我增加到15秒)。
另外,请注意我在它们连接后添加了一个销毁。您让那些套接字打开,当只有几个连接时,这在这里工作正常,但如果连接很多端口,则可能会出现问题。
注意事项:在测试这个程序时,我运行了很多次,在我运行它大约第 20 次之后,我的互联网连接中断了一段时间,并且需要重新启动我的电缆调制解调器让事情再次正常工作。我怀疑电缆调制解调器内部的某些东西在测试时无法一次又一次地处理这种快速的请求洪流。但是,康卡斯特也可能由于异常活动(大量端口请求)而关闭了我的连接。
当然,这可能都是巧合,而我的小中断可能与我正在做的事情无关,但时间似乎太紧密了,我认为这只是巧合。
可以进一步修改此程序,以通过每秒计量不超过 N 个端口探测来“减慢速度”。