Node.js 実践:socket.address() を活用したサーバー構築とデバッグ

2025-04-26

具体的には、socket.address() が返すオブジェクトは、通常以下のプロパティを持ちます。

  • family: アドレスファミリです。IPv4の場合は 'IPv4'、IPv6の場合は 'IPv6' が返されます。
  • address: ソケットがバインドされているローカルIPアドレスです。これは、IPv4アドレス(例: 127.0.0.1192.168.1.10) または IPv6アドレス(例: ::12001:db8::1) のいずれかになります。
  • port: ソケットがバインドされているローカルポート番号です。


一般的な誤解と注意点

  1. socket.address() の呼び出しタイミング:

    • 問題: ソケットがまだバインドされていない状態(例えば、サーバーが listen() を呼び出す前や、クライアントソケットがまだ接続されていない状態)で socket.address() を呼び出すと、期待される情報(portaddressfamily)が取得できない可能性があります。場合によっては nullundefined が返ることがあります。
    • トラブルシューティング: socket.address() を呼び出す前に、ソケットが適切に初期化され、バインドまたは接続されていることを確認してください。サーバーの場合は listen() コールバック内や 'listening' イベント後、クライアントの場合は 'connect' イベント後などが適切なタイミングです。
  2. サーバーが複数のインターフェースを持つ場合:

    • 問題: サーバーが複数のネットワークインターフェース(複数のIPアドレスを持つ)を持つ場合、server.address() が返すアドレスは、server.listen() で指定したアドレス、または指定しなかった場合は Node.js が選択したデフォルトのインターフェースのアドレスになります。意図しないインターフェースのアドレスが返ってくることがあります。
    • トラブルシューティング: サーバーを特定のインターフェースにバインドしたい場合は、server.listen() の第二引数にIPアドレスを指定してください。例えば、IPv4のすべてのインターフェースにバインドする場合は '0.0.0.0'、IPv6のすべてのインターフェースにバインドする場合は '::' を指定します。特定のIPアドレスにバインドする場合は、そのIPアドレスを文字列で指定します。
  3. クライアントソケットのローカルアドレス:

    • 問題: クライアントソケットの場合、socket.address() が返すのはクライアント側のローカルアドレスとポートです。これは、接続先のサーバーのアドレスとは異なります。クライアント側の特定のアドレスやポートを使用したい場合は、net.connect() のオプションで localAddresslocalPort を指定できますが、指定しない場合は OS が自動的に割り当てます。
    • トラブルシューティング: クライアントがどのローカルアドレスとポートを使用しているかを確認するために socket.address() を使用するのは適切ですが、サーバーのアドレスと混同しないように注意してください。サーバーのアドレスは、接続時に使用した情報や、サーバー側から socket.remoteAddress などで取得できます。
  4. IPv6 アドレスの扱い:

    • 問題: IPv6 アドレスは IPv4 アドレスとは異なる形式を持ちます。socket.address().address が IPv6 アドレスの場合、コロン (:) を含む文字列として返されます。これを IPv4 アドレスとして処理しようとするとエラーが発生する可能性があります。
    • トラブルシューティング: socket.address().family プロパティを確認し、アドレスファミリが 'IPv6' である場合は、IPv6 アドレスとして適切に処理するようにしてください。
  5. 権限の問題 (ポート番号):

    • 問題: 特に Unix 系システムでは、1024番以下のポート番号は通常 root 権限を持つプロセスしかバインドできません。一般ユーザーの Node.js プロセスがこれらのポートを使用しようとすると、EACCES (Permission denied) エラーが発生し、結果としてソケットがバインドされず、socket.address() が期待する情報を返さないことがあります。
    • トラブルシューティング: 1024番以下のポートを使用する場合は、Node.js プロセスを root 権限で実行するか、sudo コマンドを使用する必要があります。セキュリティ上の理由から、可能な限り 1024番以上のポートを使用することが推奨されます。

トラブルシューティングの一般的なアプローチ

  • ネットワーク設定の確認: ファイアウォールやネットワークインターフェースの設定が、Node.js アプリケーションの動作を妨げていないかを確認します。
  • エラーハンドリング: ソケットの作成や listen()connect() などの操作でエラーが発生していないか、適切なエラーハンドリングが行われているかを確認します。
  • イベントの確認: ソケット関連のイベント ('listening', 'connect', 'error', 'close') が意図した順序で発生しているかを確認します。
  • ログ出力: socket.address() の結果をログに出力して、実際にどのような情報が取得できているかを確認します。


サーバーがリッスンしているアドレスとポートを取得する例

この例では、簡単な TCP サーバーを作成し、サーバーがリッスンを開始した後に server.address() を使用して、サーバーがバインドされたアドレスとポート情報を取得します。

const net = require('net');

const server = net.createServer((socket) => {
  console.log('クライアントが接続しました。');
  socket.end('サーバーからの応答です。\n');
});

server.listen(3000, '127.0.0.1', () => {
  const addressInfo = server.address();
  console.log('サーバーは以下の情報でリッスンしています:');
  console.log('  アドレス:', addressInfo.address);
  console.log('  ポート:', addressInfo.port);
  console.log('  ファミリ:', addressInfo.family);
});

server.on('error', (err) => {
  console.error('サーバーエラー:', err);
});

このコードを実行すると、コンソールに以下のような出力が表示されます。

サーバーは以下の情報でリッスンしています:
  アドレス: 127.0.0.1
  ポート: 3000
  ファミリ: IPv4

server.listen() の第二引数に '0.0.0.0' を指定すると、すべての利用可能な IPv4 インターフェースでリッスンし、第三引数を省略すると Node.js が利用可能な最初のポートを選択します。その場合、server.address() は実際にバインドされたアドレスとポートを返します。

クライアントソケットのローカルアドレスとポートを取得する例

この例では、TCP クライアントを作成し、サーバーに接続した後、クライアントソケットのローカルアドレス情報を socket.address() を使用して取得します。

const net = require('net');

const client = net.connect({ port: 3000, host: '127.0.0.1' }, () => {
  console.log('サーバーに接続しました。');

  const localAddressInfo = client.address();
  console.log('クライアントのローカルアドレス情報:');
  console.log('  アドレス:', localAddressInfo.address);
  console.log('  ポート:', localAddressInfo.port);
  console.log('  ファミリ:', localAddressInfo.family);

  client.end();
});

client.on('end', () => {
  console.log('サーバーとの接続を閉じました。');
});

client.on('error', (err) => {
  console.error('クライアントエラー:', err);
});

事前に上記のサーバーの例を実行しておき、このクライアントの例を実行すると、クライアント側のローカルアドレスと OS が自動的に割り当てたポート番号が表示されます。出力例は以下のようになります(ポート番号は実行環境によって異なります)。

サーバーに接続しました。
クライアントのローカルアドレス情報:
  アドレス: 127.0.0.1
  ポート: 50345
  ファミリ: IPv4
サーバーとの接続を閉じました。

サーバー内で接続されたクライアントのローカルアドレス情報を取得する例

サーバーがクライアントからの接続を受け付けた際に、接続されたソケットオブジェクトに対して socket.address() を呼び出すことで、クライアント側のローカルアドレス情報を取得できます。

const net = require('net');

const server = net.createServer((socket) => {
  console.log('新しいクライアントが接続しました。');

  const clientLocalAddressInfo = socket.address();
  console.log('クライアントのローカルアドレス情報 (サーバー側から見た):');
  console.log('  アドレス:', clientLocalAddressInfo.address);
  console.log('  ポート:', clientLocalAddressInfo.port);
  console.log('  ファミリ:', clientLocalAddressInfo.family);

  console.log('クライアントのリモートアドレス:', socket.remoteAddress);
  console.log('クライアントのリモートポート:', socket.remotePort);

  socket.end('サーバーからの応答です。\n');
});

server.listen(3000, '127.0.0.1', () => {
  const serverAddress = server.address();
  console.log('サーバーは', serverAddress.address, 'の', serverAddress.port, '番ポートでリッスンしています。');
});

server.on('error', (err) => {
  console.error('サーバーエラー:', err);
});

この例では、クライアントがサーバーに接続すると、サーバー側のコンソールに接続されたクライアントのローカルアドレス情報と、socket.remoteAddress および socket.remotePort を使用して取得したリモートアドレス(クライアントのIPアドレス)とポート番号が表示されます。



サーバーソケット (net.Server) のアドレス情報を取得する server.address()

net.createServer() で作成したサーバーオブジェクトも address() メソッドを持っています。これは、サーバーが server.listen() を呼び出した後に、実際にリッスンしているアドレスとポートを返します。

const net = require('net');

const server = net.createServer((socket) => {
  // クライアントとの通信処理
});

server.listen(3000, () => {
  const serverAddress = server.address();
  console.log('サーバーは', serverAddress.address, ':', serverAddress.port, 'でリッスンしています。');
});

これは、サーバー自身のローカルアドレス情報を取得する点で socket.address() と似ていますが、対象が個々の接続ソケットではなく、サーバーオブジェクト自体である点が異なります。