您可能試圖一次性打開太多的連接,並且資源耗盡或在接收服務器上被阻塞,導致其氾濫。
因爲所有的套接字都是異步的,您的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次我跑了後,我的互聯網連接走下了一小會兒,它把我的電纜重新啓動調制解調器讓事情重新開始。我懷疑有線調制解調器內部的某些東西在測試過程中無法一次又一次地處理這種快速的請求。但是,由於不尋常的活動(大量的端口請求),Comcast也可能會關閉我的連接。
當然,這可能都是巧合,我的小故障可能與我正在做的事情沒有關係,但時機似乎太相關了,我認爲這只是一個巧合。
這個程序可以通過計算出每秒鐘不超過N個端口探頭進一步修改以「減慢速度」。
在錯誤和超時回調中添加一個console.log。嘗試趕上dos無助於異步代碼。 –
您可能應該增加打開文件描述符的最大數量。這取決於操作系統。 – lipp