Node.js socket.autoSelectFamilyAttemptedAddresses のエラーとトラブルシューティング

2025-04-26

より具体的に説明すると、以下のようになります。

  1. デュアルスタックソケット
    Node.js でネットワーク接続を行う際、特にサーバーを作成する場合などに、IPv6 と IPv4 の両方のアドレスでクライアントからの接続を受け付けられるように設定することがあります。これがデュアルスタックソケットです。

  2. 接続試行
    デュアルスタックソケットが特定のアドレス(ホスト名や IP アドレス)に接続しようとする際、その名前解決の結果として複数の IP アドレス(IPv6 と IPv4 の両方)が得られることがあります。

  3. autoSelectFamilyAttemptedAddresses プロパティ
    このプロパティは、Node.js が接続を試みた IP アドレスのリストを記録します。接続に失敗した場合や、接続確立までの過程でどのようなアドレスが試されたかを知ることができます。この配列には、試行された各 IP アドレスが文字列として格納されます。

このプロパティの主な用途としては、以下のようなものが考えられます。

  • 接続の挙動の理解
    デュアルスタック環境において、Node.js がどのように接続を試みているのかを理解するのに役立ちます。例えば、IPv6 アドレスが優先的に試行される設定になっているかなどを確認できます。
  • 接続エラーのデバッグ
    接続に失敗した場合、このプロパティを確認することで、どの IP アドレスへの接続が試みられたのか、そしてその結果がどうだったのか(例えば、タイムアウトなど)を把握し、問題の原因究明に役立てることができます。

簡単な例

const net = require('net');

const socket = net.connect({ host: 'example.com', port: 80 }, () => {
  console.log('接続しました!');
  socket.end();
});

socket.on('connect', () => {
  console.log('接続成功時の attemptedAddresses:', socket.autoSelectFamilyAttemptedAddresses);
});

socket.on('error', (err) => {
  console.error('接続エラー:', err);
  console.log('エラー発生時の attemptedAddresses:', socket.autoSelectFamilyAttemptedAddresses);
});

この例では、example.com への接続を試み、接続成功時とエラー発生時の socket.autoSelectFamilyAttemptedAddresses の内容をコンソールに出力しています。これにより、どのような IP アドレスへの接続が試みられたかを確認できます。



一般的なエラーとトラブルシューティング

    • エラー
      接続を試みた全てのアドレスでタイムアウトが発生し、接続に失敗する。
    • socket.autoSelectFamilyAttemptedAddresses の情報
      このプロパティを確認すると、接続を試みた IPv6 アドレスと IPv4 アドレスのリストが表示されます。
    • トラブルシューティング
      • ネットワーク疎通の確認
        試みられた各 IP アドレスに対して、ネットワーク経由で到達可能か (ping など) を確認します。ファイアウォールやネットワーク設定が原因で接続をブロックしている可能性があります。
      • サーバー側の確認
        接続先のサーバーが起動しているか、指定されたポートでリッスンしているかを確認します。
      • DNS 解決の確認
        ホスト名で接続している場合、DNS 解決が正しく行われているかを確認します。誤った IP アドレスに接続を試みている可能性があります。
      • タイムアウト設定の確認
        connect() メソッドなどで設定しているタイムアウト時間が適切かどうかを見直します。ネットワーク環境によっては、より長いタイムアウトが必要な場合があります。
  1. 接続拒否 (Connection Refused)

    • エラー
      接続を試みたアドレスで接続が拒否される。
    • socket.autoSelectFamilyAttemptedAddresses の情報
      接続を試みた IP アドレスのリストが表示されます。
    • トラブルシューティング
      • サーバー側の確認
        接続先のサーバーが起動しているか、指定されたポートでリッスンしているかを確認します。サーバーが起動していない場合や、異なるポートでリッスンしている可能性があります。
      • ファイアウォールの確認
        クライアントまたはサーバーのファイアウォールが接続をブロックしていないかを確認します。
      • IP アドレスとポートの確認
        connect() メソッドで指定した IP アドレスとポートが正しいかを確認します。
  2. ホストに到達できない (Cannot reach host)

    • エラー
      指定されたホスト名に対応する IP アドレスに到達できない。
    • socket.autoSelectFamilyAttemptedAddresses の情報
      DNS 解決は成功しているものの、リストされた IP アドレスへの接続が全く試行されていない、または試行に失敗している可能性があります。
    • トラブルシューティング
      • ネットワークインターフェースの確認
        クライアント側のネットワークインターフェースが正常に動作しているかを確認します。
      • ルーティングの確認
        クライアントからサーバーへのネットワーク経路が正しく設定されているかを確認します。
      • DNS サーバーの確認
        DNS サーバーの設定が正しく、名前解決が正常に行われているかを確認します。
  3. IPv6/IPv4 の優先順位に関する問題

    • エラー
      デュアルスタック環境で、意図しない IP プロトコル (IPv6 または IPv4) で接続が確立される、または接続に失敗する。
    • socket.autoSelectFamilyAttemptedAddresses の情報
      試行された IP アドレスの順序や種類を確認することで、Node.js がどのような優先順位で接続を試みているかを把握できます。
    • トラブルシューティング
      • システムの設定確認
        OS レベルでの IPv6/IPv4 の優先順位設定が意図した通りになっているかを確認します。
      • family オプションの明示的な指定
        net.connect()new net.Socket() のオプションで family: 4 (IPv4) または family: 6 (IPv6) を明示的に指定することで、特定の IP プロトコルでの接続を強制できます。
      • エラーメッセージの確認
        接続エラーが発生した場合、具体的なエラーメッセージ (例えば、"Address family not supported") を確認し、原因を特定します。

socket.autoSelectFamilyAttemptedAddresses を活用したトラブルシューティングのヒント

  • 特定の IP アドレス (例えば IPv6 のみ) で接続が失敗する場合、そのプロトコルに関するネットワーク設定やサーバー側の対応状況を確認します。
  • 全てのアドレスでの接続が失敗している場合、ネットワーク全体の疎通性やサーバー側の状態を確認する必要があります。
  • 試みられた IP アドレスが意図したものと異なる場合、DNS 解決やネットワーク設定に問題がある可能性があります。
  • エラーが発生した場合、まずこのプロパティの内容をログ出力するなどして確認し、どのような IP アドレスへの接続が試みられたのかを把握します。


例 1: 接続試行のログ出力

この例では、指定されたホストとポートに接続を試み、接続成功時とエラー発生時に socket.autoSelectFamilyAttemptedAddresses の内容をコンソールに出力します。

const net = require('net');

const host = 'example.com';
const port = 80;

const socket = net.connect({ host: host, port: port }, () => {
  console.log(`[成功] ${host}:${port} に接続しました!`);
  console.log('接続成功時の attemptedAddresses:', socket.autoSelectFamilyAttemptedAddresses);
  socket.end();
});

socket.on('connect', () => {
  // connect イベントは接続が確立した後に発生します
});

socket.on('error', (err) => {
  console.error(`[エラー] ${host}:${port} への接続エラー:`, err.message);
  console.log('エラー発生時の attemptedAddresses:', socket.autoSelectFamilyAttemptedAddresses);
});

socket.on('end', () => {
  console.log('ソケットを閉じます。');
});

この例のポイント

  • error イベントハンドラは、接続に失敗した場合に実行されます。ここでも socket.autoSelectFamilyAttemptedAddresses を確認することで、どの IP アドレスへの接続が試みられたかを知ることができます。
  • connect イベントハンドラは、接続が成功した後に実行されます。この時点で socket.autoSelectFamilyAttemptedAddresses を確認できます。
  • net.connect() を使用して指定されたホストとポートへの接続を試みます。

例 2: 接続試行されたアドレスの監視

この例では、接続を試みたアドレスのリストを監視し、特定のアドレスへの接続が試みられた場合にログを出力します。

const net = require('net');

const host = 'example.com';
const port = 80;

const socket = net.connect({ host: host, port: port }, () => {
  console.log(`[成功] ${host}:${port} に接続しました!`);
  console.log('接続成功時の attemptedAddresses:', socket.autoSelectFamilyAttemptedAddresses);
  socket.end();
});

socket.on('lookup', (err, addresses, family, hostname) => {
  if (err) {
    console.error('DNS lookup エラー:', err);
    return;
  }
  console.log('DNS lookup 結果:', addresses);
});

socket.on('connect', () => {
  // ...
});

socket.on('error', (err) => {
  // ...
});

socket.on('end', () => {
  // ...
});

socket.on('attempt', (address, family, port, host) => {
  console.log(`[試行] ${host}:${port} (${family}), アドレス: ${address}`);
});

socket.on('autoSelectFamilyAttemptedAddressesChange', (addresses) => {
  console.log('autoSelectFamilyAttemptedAddresses が変更されました:', addresses);
  if (addresses.includes('2001:db8::1')) {
    console.log('特定の IPv6 アドレスへの接続試行が検出されました!');
  }
});

この例のポイント

  • この例では、特定の IPv6 アドレス (2001:db8::1) への接続試行が検出された場合に特別なログを出力しています。
  • socket.on('autoSelectFamilyAttemptedAddressesChange', ...) イベントを使用すると、socket.autoSelectFamilyAttemptedAddresses プロパティが変更されたときに通知を受け取ることができます。これにより、接続試行の進行状況を監視できます。
  • socket.on('attempt', ...) イベントを使用すると、個々の接続試行が発生するたびに情報を受け取ることができます。

例 3: 接続失敗時の試行アドレスの確認とエラー分析

この例では、接続に失敗した場合に socket.autoSelectFamilyAttemptedAddresses を確認し、試行されたアドレスに基づいてエラー分析を行います。

const net = require('net');

const host = 'nonexistent.example.com'; // 存在しないホスト
const port = 80;

const socket = net.connect({ host: host, port: port, timeout: 2000 }, () => {
  console.log(`[成功] ${host}:${port} に接続しました!`);
  socket.end();
});

socket.on('timeout', () => {
  console.log('接続タイムアウトしました。');
  console.log('タイムアウト時の attemptedAddresses:', socket.autoSelectFamilyAttemptedAddresses);
  socket.destroy();
});

socket.on('error', (err) => {
  console.error(`[エラー] ${host}:${port} への接続エラー:`, err.message);
  console.log('エラー発生時の attemptedAddresses:', socket.autoSelectFamilyAttemptedAddresses);
  const attemptedAddresses = socket.autoSelectFamilyAttemptedAddresses;
  if (attemptedAddresses && attemptedAddresses.length > 0) {
    console.log('試行されたアドレス:', attemptedAddresses);
    // ここで試行されたアドレスに基づいて、例えばネットワークの問題を疑うなどのエラー分析を行うことができます。
  }
});

socket.on('end', () => {
  console.log('ソケットを閉じます。');
});
  • エラーハンドラ内では、socket.autoSelectFamilyAttemptedAddresses の内容に基づいて、追加のエラー分析を行うことができます。
  • timeout イベントと error イベントの両方で socket.autoSelectFamilyAttemptedAddresses を確認し、どのようなアドレスへの接続が試みられた後にタイムアウトやエラーが発生したのかを把握します。
  • 存在しないホストに接続を試みることで、接続エラーを意図的に発生させています。