【Node.js】socket.address()でよくあるエラーと解決策:EADDRINUSEからECONNREFUSEDまで
Node.js における socketaddress.address
は、ネットワーク通信で使用されるソケットのアドレス情報の一部です。具体的には、そのソケットが関連付けられている IPアドレス(IPv4 または IPv6) を表す文字列を返します。
Node.js の net
モジュールや dgram
モジュールなどでソケットを扱う際に、ソケットのローカルアドレスやリモートアドレスの情報を取得するために使われます。
具体的な使用例と意味
socket.address()
メソッドを呼び出すと、以下のようなオブジェクトが返されます。
{
address: '127.0.0.1', // または '::1' (IPv6の場合)
family: 'IPv4', // または 'IPv6'
port: 12345
}
このオブジェクトの中の address
プロパティが、IPアドレスに相当します。
port
: ソケットのポート番号。family
: IPアドレスのファミリー('IPv4' または 'IPv6')。address
: ソケットのIPアドレス(例: '127.0.0.1' や '192.168.1.100'、IPv6の場合は '::1' など)。
socketaddress.address
が使われる一般的なケース
-
サーバーのリスニングアドレスの確認:
net.Server
のlisten()
メソッドでサーバーを起動した後、server.address().address
を使うことで、実際にサーバーがどのIPアドレスで接続を待ち受けているかを確認できます。例えば、0.0.0.0
は「すべてのネットワークインターフェースからの接続を受け入れる」ことを意味します。 -
クライアント接続のリモートアドレスの取得:
net.Socket
オブジェクトには、接続しているリモート側のIPアドレスを取得するためのsocket.remoteAddress
プロパティがあります。これは基本的にsocket.address().address
とは異なり、接続相手のアドレスを示します。ただし、一部の状況(例えば、リバースプロキシの背後にある場合など)では、実際のクライアントIPではなく、プロキシのIPが返されることがあります。 -
UDPソケットの送信元・送信先アドレス:
dgram
(UDP) モジュールでは、socket.address().address
はUDPソケットのローカルIPアドレスを指し、受信イベントのコールバックで渡されるrinfo.address
は送信元(リモート)のIPアドレスを指します。
- ウェブアプリケーションでクライアントのIPアドレスを取得する場合、
req.socket.remoteAddress
を使うのが一般的ですが、前述のようにプロキシを介している場合はreq.headers['x-forwarded-for']
などのヘッダーを確認する必要がある場合があります。 socket.address()
は、ソケットがバインドされている(または接続されている)場合にのみ意味のある値を返します。ソケットがまだ何も操作されていない状態では、デフォルト値や空の値が返されることがあります。
socket.address()
は、ソケットが実際にアドレスにバインドされている場合にのみ有効な情報を返します。そのため、適切なタイミングで呼び出さないと問題が発生することがあります。
socket.address() が undefined または null を返す
-
トラブルシューティング:
-
サーバー側:
server.listen()
のコールバック関数内、または'listening'
イベントリスナー内でserver.address()
を呼び出してください。const net = require('net'); const server = net.createServer((socket) => { // 接続されたクライアントソケットのリモートアドレス console.log('Client connected from:', socket.remoteAddress, socket.remotePort); }); server.listen(0, () => { // ポート0はOSに空きポートを自動割り当てさせる const address = server.address(); console.log('Server listening on:', address.address, address.port); // ここで取得 });
-
クライアント側:
socket.connect()
のコールバック関数内、または'connect'
イベントリスナー内でsocket.localAddress
やsocket.remoteAddress
を参照してください。
-
-
原因:
- サーバー側:
server.listen()
のコールバックが実行される前や'listening'
イベントが発生する前にserver.address()
を呼び出している。ソケットがまだ OS によってポートにバインドされていないため、アドレス情報が存在しません。 - クライアント側:
socket.connect()
のコールバックが実行される前や'connect'
イベントが発生する前にsocket.localAddress
やsocket.remoteAddress
を参照している。接続が確立されていないため、アドレス情報が利用できません。 - ユニックスドメインソケット: TCP/IP ソケットではなくユニックスドメインソケット(ファイルパスを使用するソケット)を使用している場合、
address
プロパティは存在しません。
- サーバー側:
const net = require('net'); const client = new net.Socket();
client.connect(8080, 'localhost', () => {
console.log('Connected to server.');
console.log('Local address:', client.localAddress, client.localPort);
console.log('Remote address:', client.remoteAddress, client.remotePort);
});
client.on('error', (err) => {
console.error('Client error:', err);
});
```
* **ユニックスドメインソケットの場合**: `path` プロパティや `server.address()` が返すオブジェクトの他のプロパティを確認してください。
EADDRINUSE (Address already in use)
-
トラブルシューティング:
- ポートの変更: 最も簡単な解決策は、サーバーが listen するポート番号を変更することです。
- プロセスIDの特定と終了:
- Linux/macOS:
lsof -i tcp:<ポート番号>
(例:lsof -i tcp:3000
) でポートを使用しているプロセスを特定し、kill <PID>
で終了させます。 - Windows:
netstat -ano | findstr :<ポート番号>
(例:netstat -ano | findstr :3000
) でPIDを特定し、taskkill /PID <PID> /F
で終了させます。
- Linux/macOS:
- サーバーの再起動: 以前のプロセスが完全に終了していない可能性があるので、少し待ってからサーバーを再起動してみます。
- エラーハンドリング:
server.on('error', (err) => { if (err.code === 'EADDRINUSE') { /* 処理 */ } });
のようにエラーを捕捉し、再試行ロジックを実装することもできます。
-
原因:
- サーバーが listen しようとしているポートが、既に他のプロセスによって使用されている。これはNode.jsアプリケーションの別のインスタンスが既に実行されている場合や、他のアプリケーションが同じポートを使用している場合によく発生します。
EADDRNOTAVAIL (Cannot assign requested address)
-
トラブルシューティング:
-
IPアドレスの確認: 指定したIPアドレスが、アプリケーションを実行しているサーバーに実際に割り当てられているIPアドレスであることを確認してください。
-
0 (IPv4) または :: (IPv6) の使用: 特定のIPアドレスに限定せず、すべてのネットワークインターフェースからの接続を受け入れる場合は、
0.0.0.0
(IPv4) または::
(IPv6) をホストとして指定します。これはサーバーアプリケーションで一般的です。server.listen(3000, '0.0.0.0', () => { console.log('Server listening on all interfaces on port 3000'); });
-
-
原因:
- 指定されたIPアドレス(
host
またはaddress
)が、システム上のどのネットワークインターフェースにも関連付けられていない場合。例えば、存在しないIPアドレスにバインドしようとしたり、許可されていないIPアドレスを使用しようとしたりする場合です。
- 指定されたIPアドレス(
ECONNREFUSED (Connection refused)
-
トラブルシューティング:
- サーバーが実行中か確認: 接続しようとしているサーバーが実際に起動しており、指定されたポートでリッスンしていることを確認します。
- ポート番号とIPアドレスの確認: クライアントとサーバーで指定しているIPアドレスとポート番号が一致しているか確認します。
- ファイアウォール設定: サーバー側のファイアウォールが、指定されたポートへの接続をブロックしていないか確認します。
- ホスト名の確認: ホスト名を使用している場合、
ping
コマンドなどでそのホスト名が正しいIPアドレスに解決されるかを確認します。
-
原因:
- クライアントが接続しようとしたIPアドレスとポートで、サーバーがリッスンしていない場合。
- ファイアウォールが接続をブロックしている場合。
- 指定されたホスト名が間違っているか、DNS解決に失敗している場合。
ENOTFOUND (DNS lookup failed)
-
トラブルシューティング:
- ホスト名のスペルミス: ホスト名にスペルミスがないか確認します。
- DNS設定: ネットワークのDNS設定が正しいか、またはDNSサーバーが正しく機能しているか確認します。
- インターネット接続: インターネット接続が利用可能か確認します(リモートホストに接続する場合)。
-
原因:
- クライアントが接続しようとしているホスト名(例:
example.com
)がDNSによって解決できない場合。つまり、そのホスト名に対応するIPアドレスが見つからない場合です。
- クライアントが接続しようとしているホスト名(例:
-
ログ出力:
console.log()
を活用して、ソケットの状態や取得されたアドレス情報を随時出力し、デバッグの助けとします。 -
非同期処理の理解:
socket.address()
やserver.address()
は、ソケットがバインドまたは接続された後にのみ有効な値を返します。Node.js の非同期の性質を理解し、適切なイベントハンドラ内でこれらのプロパティを参照するようにしてください。 -
エラーイベントの監視: Node.js のソケットおよびサーバーは
'error'
イベントを発行します。このイベントを常にリッスンし、適切にエラーを処理することが重要です。これにより、予期せぬクラッシュを防ぐことができます。server.on('error', (err) => { console.error('Server error:', err.code, err.message); // エラーコードに基づいて適切な処理を行う }); client.on('error', (err) => { console.error('Client socket error:', err.code, err.message); // 接続再試行などの処理 });
サーバー側での server.address() の使用例
サーバーがどのIPアドレスとポートでリッスンしているかを確認する一般的な例です。
// server.js
const net = require('net');
const PORT = 3000;
const HOST = '0.0.0.0'; // すべてのネットワークインターフェースからの接続を受け入れる
const server = net.createServer((socket) => {
// クライアントが接続した際にログを出力
console.log(`クライアントが接続しました: ${socket.remoteAddress}:${socket.remotePort}`);
// クライアントからのデータを受信
socket.on('data', (data) => {
console.log(`クライアントからデータを受信: ${data.toString()}`);
socket.write(`サーバーからの応答: ${data.toString()}`);
});
// クライアントが切断した際にログを出力
socket.on('end', () => {
console.log('クライアントが切断しました。');
});
// ソケットエラーハンドリング
socket.on('error', (err) => {
console.error(`ソケットエラー: ${err.message}`);
});
});
// サーバーがリスニングを開始する
server.listen(PORT, HOST, () => {
const addressInfo = server.address(); // ここで server.address() を呼び出す
console.log('--- サーバー情報 ---');
console.log(`サーバーは ${addressInfo.family} 上の ${addressInfo.address}:${addressInfo.port} でリッスンしています。`);
console.log('--------------------');
console.log(`サーバープロセスID: ${process.pid}`);
});
// サーバーエラーハンドリング
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`エラー: ポート ${PORT} は既に使用されています。`);
console.error('他のプロセスがこのポートを使用しているか確認してください。');
} else {
console.error(`サーバーエラー: ${err.message}`);
}
});
解説:
addressInfo.address
はサーバーのIPアドレス(この場合は'0.0.0.0'
または OS が割り当てた実際の IP)、addressInfo.port
はポート番号、addressInfo.family
はアドレスファミリー('IPv4'
または'IPv6'
)を示します。- このコールバック内で
server.address()
を呼び出すことで、OSが実際にサーバーに割り当てたIPアドレスとポート番号を含むオブジェクト (addressInfo
) を取得できます。 server.listen(PORT, HOST, () => { ... });
の第3引数のコールバック関数は、サーバーが正常にリッスンを開始した後に実行されます。
クライアント側での socket.localAddress および socket.remoteAddress の使用例
クライアントソケットのローカルアドレス(自身のアドレス)と、接続先のリモートアドレス(サーバーのアドレス)を確認する例です。
// client.js
const net = require('net');
const SERVER_PORT = 3000;
const SERVER_HOST = 'localhost'; // または '127.0.0.1'
const client = new net.Socket();
client.connect(SERVER_PORT, SERVER_HOST, () => {
console.log('--- クライアント接続情報 ---');
console.log(`サーバーに接続しました: ${SERVER_HOST}:${SERVER_PORT}`);
console.log(`クライアントのローカルアドレス: ${client.localAddress}:${client.localPort}`); // 自身のIP/ポート
console.log(`接続先のリモートアドレス: ${client.remoteAddress}:${client.remotePort}`); // サーバーのIP/ポート
console.log('--------------------------');
client.write('こんにちは、サーバーさん!');
});
client.on('data', (data) => {
console.log(`サーバーからデータを受信: ${data.toString()}`);
client.end(); // データを取得したら接続を終了
});
client.on('end', () => {
console.log('サーバーとの接続が切断されました。');
});
client.on('error', (err) => {
console.error(`クライアントソケットエラー: ${err.message}`);
if (err.code === 'ECONNREFUSED') {
console.error('接続を拒否されました。サーバーが実行中か、ポートが正しいか確認してください。');
} else if (err.code === 'ENOTFOUND') {
console.error('ホストが見つかりません。ホスト名が正しいか確認してください。');
}
});
client.on('close', () => {
console.log('ソケットが閉じられました。');
});
解説:
client.remoteAddress
とclient.remotePort
: 接続先であるサーバーのIPアドレスとポート番号です。client.localAddress
とclient.localPort
: クライアント自身が使用しているローカルのIPアドレスとポート番号です。OSが自動的に割り当てます。client.connect(SERVER_PORT, SERVER_HOST, () => { ... });
のコールバック関数は、クライアントがサーバーへの接続を確立した後に実行されます。
dgram
モジュール(UDPソケット)の場合も同様に socket.address()
を使用できます。これは、送信側と受信側の両方で、自身が使用しているアドレスを確認するために使われます。
UDP サーバー (受信側)
// udp_server.js
const dgram = require('dgram');
const server = dgram.createSocket('udp4'); // IPv4 UDPソケットを作成
const UDP_PORT = 41234;
const UDP_HOST = '0.0.0.0'; // すべてのインターフェースからの受信
server.on('error', (err) => {
console.error(`サーバーエラー:\n${err.stack}`);
server.close();
});
server.on('message', (msg, rinfo) => {
console.log(`サーバーが ${rinfo.address}:${rinfo.port} からデータを受信しました: ${msg}`);
// 受信したデータに対する応答を同じ送信元に送り返す
server.send(`ACK: ${msg}`, rinfo.port, rinfo.address, (err) => {
if (err) console.error(`応答送信エラー: ${err.message}`);
});
});
server.on('listening', () => {
const address = server.address(); // ここでサーバーのアドレス情報を取得
console.log(`UDPサーバーは ${address.family} 上の ${address.address}:${address.port} でリッスンしています。`);
});
server.bind(UDP_PORT, UDP_HOST);
解説:
rinfo
オブジェクトは、データを受信した送信元(リモート)の情報を含んでいます。rinfo.address
とrinfo.port
がそれです。server.bind(UDP_PORT, UDP_HOST);
の後、'listening'
イベントでserver.address()
を呼び出します。
UDP クライアント (送信側)
// udp_client.js
const dgram = require('dgram');
const client = dgram.createSocket('udp4');
const MESSAGE = Buffer.from('こんにちは、UDPサーバー!');
const SERVER_PORT = 41234;
const SERVER_HOST = 'localhost'; // または '127.0.0.1'
client.send(MESSAGE, SERVER_PORT, SERVER_HOST, (err) => {
if (err) {
console.error(`メッセージ送信エラー: ${err.message}`);
client.close();
return;
}
console.log(`メッセージを ${SERVER_HOST}:${SERVER_PORT} へ送信しました。`);
const localAddress = client.address(); // ここでクライアントソケットのローカルアドレスを取得
console.log(`クライアントのローカルアドレス: ${localAddress.address}:${localAddress.port}`);
});
client.on('message', (msg, rinfo) => {
console.log(`サーバー ${rinfo.address}:${rinfo.port} から応答を受信しました: ${msg}`);
client.close(); // 応答を受信したらソケットを閉じる
});
client.on('error', (err) => {
console.error(`クライアントエラー:\n${err.stack}`);
client.close();
});
client.on('close', () => {
console.log('UDPクライアントソケットが閉じられました。');
});
解説:
client.send()
のコールバック関数内や、他の場所でclient.address()
を呼び出すことで、UDPクライアントがメッセージを送信する際に使用しているローカルIPアドレスとポート番号を取得できます。
しかし、特定の状況や目的のために、これらの直接的な方法の**代替となる「情報取得の経路」**や、関連するが異なる情報源が存在します。これらは厳密な意味での「代替メソッド」というよりは、「IPアドレスやポートに関する情報を得る別の手段」と捉えるのが適切です。
HTTP サーバーにおけるクライアントのIPアドレス取得 (req.socket.remoteAddress, x-forwarded-for)
ウェブアプリケーション(HTTPサーバー)の場合、通常は net.Socket
を直接操作するよりも、http
モジュールや Express.js などのフレームワークを使用します。この場合、クライアントのIPアドレスはリクエストオブジェクト (req
) から取得します。
DNSルックアップ (dns モジュール)
これは socket.address
とは直接関係ありませんが、ホスト名からIPアドレスを取得するという意味では関連性があります。socket.address().address
はソケットが現在使用しているIPアドレスを返しますが、dns
モジュールは特定のホスト名が解決されるべきIPアドレスをプログラムで取得するのに使用されます。
-
dns.resolve(hostname, [rrtype], callback)
:- より高度な DNS クエリを実行し、指定されたリソースレコードタイプ(Aレコード、AAAAレコードなど)の情報を取得します。
const dns = require('dns'); dns.lookup('nodejs.org', (err, address, family) => { if (err) { console.error('DNS ルックアップエラー:', err); return; } console.log(`nodejs.org のIPアドレス: <span class="math-inline">\{address\} \(</span>{family})`); }); dns.resolve4('example.com', (err, addresses) => { if (err) { console.error('DNS resolve4 エラー:', err); return; } console.log(`example.com のIPv4アドレス: ${addresses}`); });
関連性: 例えば、クライアントが接続しようとしているホスト名が正しいIPアドレスに解決されているかを確認するために使えます。
-
dns.lookup(hostname, callback)
:- ホスト名(例:
'www.google.com'
)に対応する最初のIPアドレスを取得します。
- ホスト名(例:
これも socket.address
とは直接関係ありませんが、Node.js プロセスが実行されているサーバー自身のローカルIPアドレス情報を取得する際に利用できます。socket.localAddress
はソケットがバインドされている特定のIPアドレスを返しますが、os.networkInterfaces()
はシステム上のすべてのネットワークインターフェースとそのIPアドレス(IPv4/IPv6)のリストを返します。
-
os.networkInterfaces()
:- システム上のすべてのネットワークインターフェース情報をオブジェクトとして返します。各インターフェースには、そのインターフェースに割り当てられたアドレスの配列が含まれます。
const os = require('os'); const interfaces = os.networkInterfaces(); console.log('--- ネットワークインターフェース情報 ---'); for (const name of Object.keys(interfaces)) { for (const iface of interfaces[name]) { // 外部に公開されているIPv4/IPv6アドレスのみを表示(内部ループバックは除く) if ('IPv4' === iface.family && !iface.internal) { console.log(`${name}: ${iface.address}`); } if ('IPv6' === iface.family && !iface.internal) { console.log(`${name} (IPv6): ${iface.address}`); } } } console.log('------------------------------------');
関連性: 例えば、サーバーアプリケーションで利用可能なすべてのローカルIPアドレスを列挙し、特定のインターフェースにバインドするかどうかを決定する際に役立ちます。
-
os.networkInterfaces()
: Node.js プロセスが実行されているマシン自体のすべてのネットワークインターフェースの IP アドレスを列挙するためのもので、ソケットが使用するローカルアドレスの選択肢を確認するのに役立つ。 -
dns
モジュール: ホスト名から IP アドレスを解決するためのもので、ソケットのアドレス情報とは異なるが関連する機能。 -
X-Forwarded-For
ヘッダー (HTTPサーバー): リバースプロキシ経由で接続された場合の真のクライアント IP を取得する(ただし信頼性検証が必要)。 -
req.socket.remoteAddress
(HTTPサーバー): HTTP リクエストを発信したクライアントの直接的な IP アドレスを取得するが、プロキシの背後にいる場合はプロキシの IP を返す。 -
socket.address()
(net
,dgram
モジュール): ソケット自身が実際にバインドされている(または接続先として認識している)IPアドレスとポートを直接的かつ正確に取得する標準的な方法。これが最も一般的で推奨される方法です。