Node.js TCPポートスキャナーの実装 - net.Socketとタイムアウト処理

問題

特定のホストのポートが開いているか確認したい。pingはICMPなのでポート単位の確認ができず、外部ライブラリを入れるほどの作業でもありません。

解決方法

const net = require('net');

async function checkPort(host, port, timeout = 500) {
  return new Promise((resolve) => {
    const socket = new net.Socket();

    const timer = setTimeout(() => {
      socket.destroy();
      resolve({ host, port, open: false });
    }, timeout);

    socket.on('connect', () => {
      clearTimeout(timer);
      socket.destroy();
      resolve({ host, port, open: true });
    });

    socket.on('error', () => {
      clearTimeout(timer);
      resolve({ host, port, open: false });
    });

    socket.connect(port, host);
  });
}

// 使用例:複数ポートを同時スキャン
async function scanPorts(host, ports) {
  const results = await Promise.all(
    ports.map(port => checkPort(host, port))
  );
  return results.filter(r => r.open);
}

ポイント

  • rejectの代わりにresolveで統一するのがポイントです。ポートが閉じているのはエラーではなく正常な結果なので、Promise.allが途中で停止しません。
  • タイムアウトはLAN環境では500msで十分です。インターネットのホストをスキャンする場合は2000〜3000msに増やす必要があります。
  • socket.destroy()を必ず呼び出してください。呼ばないとソケットが開いたままになり、数百ポートを同時スキャンするとファイルディスクリプタが枯渇します。