【Node.js】server.address() でサーバーのIPとポートを確認する方法
もう少し詳しく見ていきましょう。
server.address() が返すオブジェクトの構造
{
port: 8080, // サーバーがリッスンしているポート番号
address: '127.0.0.1', // サーバーがリッスンしている IP アドレス
family: 'IPv4' // アドレスファミリ ('IPv4' または 'IPv6')
}
このメソッドはどのような時に役立つのでしょうか?
-
動的なポート割り当ての確認
サーバーを起動する際に特定のポートを指定せず、オペレーティングシステムに自動的に割り当ててもらう場合があります(ポート番号に0
を指定するなど)。server.address()
を呼び出すことで、実際に割り当てられたポート番号を確認できます。 -
リッスンしているアドレスの確認
サーバーが特定のアドレスにバインドされているかを確認できます。例えば、複数のネットワークインターフェースを持つマシンで、特定のア IP アドレスでリッスンしているかを確認する際に役立ちます。 -
サーバー情報のログ出力
サーバーが起動した際に、リッスンしているアドレスとポート番号をログに出力することで、運用時のトラブルシューティングや確認に役立ちます。
簡単なコード例
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World!\n');
});
server.listen(8080, '127.0.0.1', () => {
const addressInfo = server.address();
console.log(`サーバーは http://${addressInfo.address}:${addressInfo.port} でリッスンしています。`);
});
この例では、サーバーが 127.0.0.1
の 8080
ポートでリッスンを開始した後、server.address()
を呼び出してその情報を取得し、コンソールに表示しています。
もし、サーバー起動時にポート番号を 0
にして自動割り当てにした場合、server.address()
を呼び出すことで実際に割り当てられたポート番号を確認できます。
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World!\n');
});
server.listen(0, '127.0.0.1', () => {
const addressInfo = server.address();
console.log(`サーバーは http://${addressInfo.address}:${addressInfo.port} でリッスンしています。`);
});
一般的なシナリオとトラブルシューティング
-
- エラー
server.address()
を呼び出すタイミングが早すぎると、サーバーがまだネットワークインターフェースにバインドされていないため、null
を返すことがあります。 - トラブルシューティング
server.listen()
メソッドのコールバック関数の中でserver.address()
を呼び出すようにしてください。listen()
のコールバックは、サーバーが正常にリッスンを開始した後に実行されます。
const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello World!\n'); }); server.listen(8080, '127.0.0.1', () => { const addressInfo = server.address(); console.log(`サーバーは http://<span class="math-inline">\{addressInfo\.address\}\:</span>{addressInfo.port} でリッスンしています。`); });
- エラー
-
サーバーがリッスンに失敗した場合
- エラー
server.listen()
がエラーで失敗した場合(例えば、指定したポートがすでに使用されているなど)、その後のserver.address()
は呼び出されないか、期待する情報が得られない可能性があります。 - トラブルシューティング
server.listen()
で発生する可能性のあるエラーを適切に処理してください。server.on('error', (err) => { ... });
を使用してエラーイベントをリッスンし、原因を特定して対処する必要があります。
const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('Hello World!\n'); }); server.on('error', (err) => { console.error('サーバー起動エラー:', err); }); server.listen(8080, '127.0.0.1', () => { const addressInfo = server.address(); console.log(`サーバーは http://<span class="math-inline">\{addressInfo\.address\}\:</span>{addressInfo.port} でリッスンしています。`); });
- エラー
-
予期しないアドレスやポート
- シナリオ
サーバー起動時にホスト名を指定した場合 (server.listen(8080, 'example.com', ...)
)、server.address().address
が解決された IP アドレスになるため、起動時のホスト名と異なる場合があります。ポート番号に0
を指定して自動割り当てにした場合、意図しないポート番号が返されることがあります。 - トラブルシューティング
起動時に指定したホスト名やポート番号と、server.address()
が返す値を比較して、意図した通りにバインドされているか確認してください。自動割り当てポートを使用する場合は、server.address().port
の値を使用して、クライアントに接続情報を提供する必要があります。
- シナリオ
-
IPv6 アドレスの扱い
- シナリオ
IPv6 アドレスでリッスンしている場合、server.address().address
は IPv6 形式の文字列になります。アドレスファミリも'IPv6'
になります。 - トラブルシューティング
IPv6 アドレスを扱う場合は、その形式を正しく処理できるようにコードを記述する必要があります。例えば、ブラウザや他のクライアントが IPv6 アドレスを正しく認識できるかなどを考慮する必要があります。
- シナリオ
-
クラスタリング環境
- シナリオ
Node.js のcluster
モジュールを使用して複数のワーカープロセスを起動している場合、各ワーカープロセスはそれぞれ独立したサーバーインスタンスを持つ可能性があります。親プロセスでserver.address()
を呼び出しても、個々のワーカーがリッスンしているアドレスとは異なる場合があります。 - トラブルシューティング
クラスタリング環境では、各ワーカープロセス内でserver.address()
を呼び出して、それぞれのリスニング情報を確認する必要があります。
- シナリオ
server.address()
自体のエラーは少ないですが、その情報を取得するタイミングや、サーバーの起動状態、ネットワーク環境などによって、期待しない結果になることがあります。以下の点に注意してトラブルシューティングを行うと良いでしょう。
- クラスタリング環境では、各ワーカープロセスで情報を確認する。
- IPv6 アドレスを使用する場合は、その形式を正しく処理する。
- 返ってきたアドレスとポートが意図したものであるか確認する。
server.listen()
のエラーハンドリングを行う。server.listen()
のコールバック内でserver.address()
を呼び出す。
例1: 基本的なサーバー情報取得
この例では、HTTP サーバーを作成し、指定したポートとアドレスでリッスンを開始した後、server.address()
を使ってサーバーのアドレス情報を取得し、コンソールに出力します。
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World!\n');
});
const port = 3000;
const host = '127.0.0.1';
server.listen(port, host, () => {
const addressInfo = server.address();
console.log(`サーバーは http://${addressInfo.address}:${addressInfo.port} でリッスンしています。`);
console.log(`アドレスファミリ: ${addressInfo.family}`);
});
解説
addressInfo
オブジェクトのaddress
(IP アドレス)、port
(ポート番号)、family
(アドレスファミリ) プロパティにアクセスして、サーバーの情報をコンソールに出力しています。- コールバック関数内で
server.address()
を呼び出し、返ってきたオブジェクトを変数addressInfo
に格納します。 server.listen(port, host, callback)
で、指定されたport
とhost
でサーバーを起動し、リッスンを開始します。コールバック関数は、サーバーが正常に起動した後に実行されます。http.createServer(...)
で基本的な HTTP サーバーを作成します。
例2: 動的に割り当てられたポートの確認
この例では、server.listen()
のポートに 0
を指定して、オペレーティングシステムに自動的にポートを割り当ててもらいます。その後、server.address()
で実際に割り当てられたポート番号を確認します。
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`アクセスありがとうございます。ポート番号: ${server.address().port}\n`);
});
server.listen(0, '127.0.0.1', () => {
const addressInfo = server.address();
console.log(`サーバーは http://${addressInfo.address}:${addressInfo.port} でリッスンしています (動的ポート)。`);
});
解説
- サーバーのレスポンス内でも
server.address().port
を使用して、クライアントに割り当てられたポート番号を通知しています。 server.listen(0, '127.0.0.1', ...)
のように、最初の引数に0
を渡すことで、利用可能なポートが自動的に割り当てられます。
例3: サーバー情報の利用 (例: 他のサービスへの通知)
この例は少し応用的なもので、サーバーが起動した後に、そのアドレス情報を何らかの他のサービス(例えば、サービスディスカバリーサーバー)に通知するようなシナリオを想定しています。
const http = require('http');
const axios = require('axios'); // 例として axios を使用
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from my service!\n');
});
const port = 4000;
const host = '0.0.0.0'; // すべての利用可能なインターフェースでリッスン
server.listen(port, host, async () => {
const addressInfo = server.address();
const serviceDiscoveryUrl = 'http://servicediscovery.example.com/register';
const registrationData = {
name: 'my-node-service',
address: addressInfo.address,
port: addressInfo.port,
};
try {
const response = await axios.post(serviceDiscoveryUrl, registrationData);
console.log('サービス登録成功:', response.data);
} catch (error) {
console.error('サービス登録失敗:', error.message);
}
console.log(`サーバーは http://${addressInfo.address}:${addressInfo.port} でリッスンしています。`);
});
- これは、マイクロサービスアーキテクチャなどで、自身のアドレス情報を中央の登録システムに通知するような場合に役立ちます。
- この例では、サーバーが起動し、
server.address()
でアドレス情報を取得した後、axios
という HTTP クライアントライブラリを使って、取得したアドレスとポート番号を含む情報をserviceDiscoveryUrl
に POST リクエストとして送信しています。
server.listen() のコールバック関数内で変数を保持する
server.listen()
を呼び出す際に、ポート番号やホスト名を引数として渡しますが、これらの値をコールバック関数内で変数に保持しておくことで、サーバーのアドレス情報を間接的に利用できます。
const http = require('http');
let serverAddress;
let serverPort;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`アクセスありがとうございます。ポート番号: ${serverPort}, アドレス: ${serverAddress}\n`);
});
const port = 8081;
const host = 'localhost';
server.listen(port, host, () => {
serverAddress = host;
serverPort = port;
console.log(`サーバーは http://${serverAddress}:${serverPort} でリッスンしています。`);
});
解説
- ただし、この方法は
server.address()
が返すアドレスファミリの情報は取得できません。また、ポートに0
を指定して動的に割り当てられた場合、コールバック内で更新する必要があります。 - これにより、コールバック関数内だけでなく、これらの変数がスコープ内であれば、サーバーのアドレス情報を間接的に参照できます。
server.listen()
に渡すport
とhost
の値を、コールバック関数内でserverPort
とserverAddress
という変数に代入しています。
動的ポート割り当ての場合の修正
const http = require('http');
let serverAddress;
let serverPort;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`アクセスありがとうございます。ポート番号: ${serverPort}, アドレス: ${serverAddress}\n`);
});
server.listen(0, 'localhost', () => {
const addressInfo = server.address();
serverAddress = addressInfo.address;
serverPort = addressInfo.port;
console.log(`サーバーは http://${serverAddress}:${serverPort} でリッスンしています (動的ポート)。`);
});
環境変数を利用する
サーバーの起動時に、ポート番号やホスト名を環境変数として設定し、アプリケーション内でそれらを参照する方法です。
PORT=8082 HOST=0.0.0.0 node your_server_file.js
const http = require('http');
const port = process.env.PORT || 3000; // 環境変数がなければデフォルト値を使用
const host = process.env.HOST || 'localhost';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`アクセスありがとうございます。ポート番号: ${port}, アドレス: ${host}\n`);
});
server.listen(port, host, () => {
console.log(`サーバーは http://${host}:${port} でリッスンしています。`);
});
解説
- 同様に、アドレスファミリの情報は直接的には取得できません。
- この方法では、サーバーのアドレス情報は起動時に外部から設定されるため、アプリケーションのコード内で直接的に取得する必要はありません。
- Node.js の
process.env
オブジェクトを通じて、これらの環境変数の値を取得し、server.listen()
の引数として使用しています。 - サーバー起動時に
PORT
とHOST
環境変数を設定しています。
設定ファイルや外部構成管理
より複雑なアプリケーションでは、サーバーの設定情報(ポート番号、ホスト名など)を JSON や YAML などの設定ファイルに記述したり、Consul や etcd などの外部構成管理サービスから取得したりすることがあります。
例 (JSON 設定ファイル)
config.json
:
{
"server": {
"port": 8083,
"host": "192.168.1.100"
}
}
server.js
:
const http = require('http');
const config = require('./config.json');
const port = config.server.port;
const host = config.server.host;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(`アクセスありがとうございます。ポート番号: ${port}, アドレス: ${host}\n`);
});
server.listen(port, host, () => {
console.log(`サーバーは http://${host}:${port} でリッスンしています。`);
});
解説
- この方法も、サーバーのアドレス情報は外部から提供されるため、
server.address()
のようなメソッドを直接呼び出す必要はありません。アドレスファミリの情報は設定ファイルに含めることも可能です。 - 設定ファイルにサーバーのポートとホスト名を記述し、アプリケーション内で読み込んで使用しています。
注意点
これらの代替方法は、server.address()
が提供するすべての情報(特に動的に割り当てられたポート番号やアドレスファミリ)を直接的に取得できるわけではありません。そのため、これらの情報が重要な場合は、やはり server.address()
を利用するのが最も確実な方法です。