Node.jsのnet.SocketAddressとは?基本から活用例まで徹底解説
new net.SocketAddress()
の役割と使い方
net.SocketAddress
クラスは、主に以下のような場面で使われます。
-
アドレス情報の表現: ソケットが接続している、または接続しようとしているネットワークアドレスを明確に定義するために使用されます。
-
オプションとしての利用:
net
モジュールの他の関数やクラス(例えば、net.BlockList
のaddAddress()
やcheck()
メソッドなど)に、アドレス情報をオブジェクトとして渡す際に利用できます。
コンストラクタの構文
new net.SocketAddress()
のコンストラクタは、オプションのオブジェクトを引数に取ることができます。
const socketAddress = new net.SocketAddress([options]);
options
オブジェクトには以下のプロパティを含めることができます。
flowlabel
:<number>
IPv6のフローラベル。デフォルトは0
。family
:<string>
IPアドレスのファミリー。'ipv4'
または'ipv6'
。デフォルトは'ipv4'
。port
:<number>
ポート番号。デフォルトは0
。address
:<string>
IPアドレス(IPv4またはIPv6)。デフォルトは'127.0.0.1'
。
使用例
引数を指定せずに net.SocketAddress
オブジェクトを作成すると、デフォルト値が設定されます。
const net = require('node:net');
const defaultAddress = new net.SocketAddress();
console.log(defaultAddress);
// 出力例: SocketAddress { address: '127.0.0.1', port: 0, family: 'ipv4', flowlabel: 0 }
オプションを指定して net.SocketAddress
オブジェクトを作成することもできます。
const net = require('node:net');
const customAddress = new net.SocketAddress({
address: '192.168.1.100',
port: 8080,
family: 'ipv4'
});
console.log(customAddress);
// 出力例: SocketAddress { address: '192.168.1.100', port: 8080, family: 'ipv4', flowlabel: 0 }
IPv6アドレスを指定する例:
const net = require('node:net');
const ipv6Address = new net.SocketAddress({
address: '::1', // または 'fe80::1' など
port: 3000,
family: 'ipv6'
});
console.log(ipv6Address);
// 出力例: SocketAddress { address: '::1', port: 3000, family: 'ipv6', flowlabel: 0 }
以前はIPアドレスとポート番号を別々の変数として渡すことが多かったかもしれませんが、net.SocketAddress
を使うことで、これらを単一のオブジェクトとして扱うことができ、APIの統一性や厳密性が高まります。
new net.SocketAddress()
自体が直接エラーを発生させることは比較的少ないですが、その引数の指定ミスや、このオブジェクトを他の net
モジュールのメソッドに渡す際に関連するエラーが発生することがあります。
TypeError: Invalid arguments または TypeError: Options must be an object
原因:
new net.SocketAddress()
のコンストラクタに、予期しない型の引数を渡した場合に発生します。例えば、オブジェクトの代わりに文字列や数値などを渡した場合です。
悪い例:
const net = require('node:net');
// オブジェクトではなく文字列を渡している
const badAddress = new net.SocketAddress('localhost:8080');
トラブルシューティング:
new net.SocketAddress()
の引数は、常にオプションのプロパティを持つプレーンなJavaScriptオブジェクトである必要があります。
修正例:
const net = require('node:net');
const goodAddress = new net.SocketAddress({
address: 'localhost',
port: 8080
});
console.log(goodAddress);
RangeError: "port" must be >= 0 and < 65536
原因:
port
プロパティに有効なポート番号の範囲外の値を指定した場合に発生します。ポート番号は 0
から 65535
の間である必要があります。
悪い例:
const net = require('node:net');
const invalidPortAddress = new net.SocketAddress({
address: '127.0.0.1',
port: 70000 // 65535 を超えている
});
トラブルシューティング:
port
には有効なポート番号(0
から 65535
)を指定します。
修正例:
const net = require('node:net');
const validPortAddress = new net.SocketAddress({
address: '127.0.0.1',
port: 8080
});
console.log(validPortAddress);
Error: Invalid IP address (または類似のメッセージ)
原因:
address
プロパティに無効なIPアドレス形式(IPv4またはIPv6)の文字列を指定した場合に発生します。
悪い例:
const net = require('node:net');
const invalidIpAddress = new net.SocketAddress({
address: 'invalid-ip-address', // 無効な形式
port: 3000
});
トラブルシューティング:
address
プロパティには、有効なIPv4アドレス(例: '192.168.1.1'
)または有効なIPv6アドレス(例: '::1'
、'2001:0db8::1'
)を指定する必要があります。ホスト名(例: 'localhost'
、'example.com'
)も受け入れられますが、内部的には解決されます。
修正例:
const net = require('node:net');
const validIpAddress = new net.SocketAddress({
address: '192.168.1.1',
port: 3000
});
console.log(validIpAddress);
const validHostnameAddress = new net.SocketAddress({
address: 'localhost',
port: 3000
});
console.log(validHostnameAddress);
TypeError: "family" must be one of "ipv4" or "ipv6"
原因:
family
プロパティに 'ipv4'
または 'ipv6'
以外の値を指定した場合に発生します。
悪い例:
const net = require('node:net');
const invalidFamilyAddress = new net.SocketAddress({
address: '127.0.0.1',
port: 8080,
family: 'udp' // 'ipv4' または 'ipv6' 以外
});
トラブルシューティング:
family
プロパティには、小文字の 'ipv4'
または 'ipv6'
を指定します。
修正例:
const net = require('node:net');
const validFamilyAddress = new net.SocketAddress({
address: '127.0.0.1',
port: 8080,
family: 'ipv4'
});
console.log(validFamilyAddress);
flowlabel の誤った指定(IPv6の場合)
原因:
flowlabel
はIPv6アドレスにのみ適用されるプロパティであり、有効な数値範囲(通常は 0
から 1048575
)を超えた値を指定した場合に予期しない動作やエラーが発生する可能性があります。また、family
が 'ipv4'
の場合に flowlabel
を指定しても無視されます。
悪い例:
const net = require('node:net');
const invalidFlowlabel = new net.SocketAddress({
address: '::1',
port: 3000,
family: 'ipv6',
flowlabel: 2000000 // 範囲外の可能性
});
トラブルシューティング:
flowlabel
はIPv6の特殊な用途で使われるため、通常はデフォルト値の 0
で問題ありません。もし設定する必要がある場合は、有効な数値範囲内であることを確認してください。
修正例:
const net = require('node:net');
const validFlowlabel = new net.SocketAddress({
address: '::1',
port: 3000,
family: 'ipv6',
flowlabel: 100 // 有効な範囲内の値
});
console.log(validFlowlabel);
net.SocketAddress
オブジェクトの検査:console.log()
を使って、作成したnet.SocketAddress
オブジェクトの中身を検査し、意図した通りのプロパティが設定されているか確認することが重要です。- Node.jsのバージョン: まれに、Node.jsのバージョンによって挙動が異なる場合があります。公式ドキュメントを参照して、使用しているNode.jsのバージョンでの
net.SocketAddress
の動作を確認してください。 - エラーメッセージの確認: Node.jsのエラーメッセージは、エラーの原因と場所を特定するのに非常に役立ちます。エラーが発生した場合は、メッセージを注意深く読み、どのプロパティが問題を引き起こしているかを特定してください。
例1: net.SocketAddress
オブジェクトの作成とプロパティの確認
この例では、new net.SocketAddress()
を使って異なる種類のアドレス情報を表現する方法と、そのプロパティにアクセスする方法を示します。
const net = require('node:net');
console.log('--- 1. デフォルトのアドレス ---');
// 引数を指定しない場合、デフォルト値(127.0.0.1:0, family: 'ipv4')が設定されます。
const defaultAddress = new net.SocketAddress();
console.log('defaultAddress:', defaultAddress);
console.log('Address:', defaultAddress.address);
console.log('Port:', defaultAddress.port);
console.log('Family:', defaultAddress.family);
console.log('Flow Label (IPv6 only):', defaultAddress.flowlabel); // IPv4なので0
console.log('\n--- 2. IPv4 アドレスの指定 ---');
// IPv4アドレスとポートを指定します。
const ipv4Address = new net.SocketAddress({
address: '192.168.1.100',
port: 8080,
family: 'ipv4' // 明示的に指定することも、省略してデフォルトにすることも可能
});
console.log('ipv4Address:', ipv4Address);
console.log('Address:', ipv4Address.address);
console.log('Port:', ipv4Address.port);
console.log('\n--- 3. IPv6 アドレスの指定 ---');
// IPv6アドレスとポートを指定します。
const ipv6Address = new net.SocketAddress({
address: '::1', // localhostのIPv6ループバックアドレス
port: 3000,
family: 'ipv6', // IPv6の場合は family: 'ipv6' を指定することが重要
flowlabel: 12345 // IPv6特有のフローラベル(通常は不要)
});
console.log('ipv6Address:', ipv6Address);
console.log('Address:', ipv6Address.address);
console.log('Port:', ipv6Address.port);
console.log('Family:', ipv6Address.family);
console.log('Flow Label (IPv6 only):', ipv6Address.flowlabel);
console.log('\n--- 4. ホスト名の指定 ---');
// アドレスにホスト名を指定することもできます(内部で解決されます)
const hostnameAddress = new net.SocketAddress({
address: 'localhost',
port: 22 // SSHポート
});
console.log('hostnameAddress:', hostnameAddress);
console.log('Address:', hostnameAddress.address); // 解決されたIPアドレスが表示される可能性があります
console.log('Port:', hostnameAddress.port);
解説:
この例では、net.SocketAddress
コンストラクタに様々なオプションを渡してオブジェクトを作成し、それぞれのプロパティが正しく設定されていることを console.log
で確認しています。特に、IPv6アドレスを指定する際には family: 'ipv6'
を指定することが重要です。
例2: net.BlockList
との組み合わせ
net.SocketAddress
は、net.BlockList
クラスと組み合わせて、特定のIPアドレスからの接続をブロックまたは許可するルールを定義する際に特に役立ちます。
この例では、net.BlockList
を使用して特定のIPv4アドレスをブロックし、別のIPv6アドレスを許可リストに追加する方法を示します。
const net = require('node:net');
// 新しい BlockList を作成
const blockList = new net.BlockList();
console.log('--- 1. IPv4アドレスをブロックリストに追加 ---');
// ブロックしたいIPv4アドレスを net.SocketAddress として定義
const blockedIpv4Addr = new net.SocketAddress({
address: '192.168.1.50',
family: 'ipv4'
});
blockList.addAddress(blockedIpv4Addr);
console.log(`'${blockedIpv4Addr.address}' をブロックリストに追加しました。`);
console.log('\n--- 2. IPv6アドレスを許可リストに追加 ---');
// 許可したいIPv6アドレスを net.SocketAddress として定義
const allowedIpv6Addr = new net.SocketAddress({
address: '2001:0db8::1',
family: 'ipv6'
});
// `addAddress()` はデフォルトでブロックリストに追加しますが、
// `check()` は許可リストの概念ではなく、特定のIPがブロックされているかを確認します。
// ここでは、blockList.addAddress() の動作を示す目的で使用します。
blockList.addAddress(allowedIpv6Addr); // この場合、これもブロックリストに追加されます
console.log(`'${allowedIpv6Addr.address}' もブロックリストに追加しました。`);
console.log('\n--- 3. アドレスがブロックされているかを確認 ---');
const testAddr1 = new net.SocketAddress({ address: '192.168.1.50', family: 'ipv4' });
const testAddr2 = new net.SocketAddress({ address: '192.168.1.51', family: 'ipv4' });
const testAddr3 = new net.SocketAddress({ address: '2001:0db8::1', family: 'ipv6' });
console.log(`'${testAddr1.address}' はブロックされていますか?`, blockList.check(testAddr1)); // true
console.log(`'${testAddr2.address}' はブロックされていますか?`, blockList.check(testAddr2)); // false
console.log(`'${testAddr3.address}' はブロックされていますか?`, blockList.check(testAddr3)); // true
解説:
この例では、net.BlockList
の addAddress()
メソッドに net.SocketAddress
オブジェクトを渡しています。これにより、IPアドレスだけでなく、そのファミリー(IPv4かIPv6か)も考慮に入れたブロックルールを設定できます。check()
メソッドも net.SocketAddress
オブジェクトを引数に取り、指定されたアドレスがブロックリストに含まれているかを判定します。
net.Server
の listen()
メソッドは、SocketAddress
オブジェクトを直接引数として受け取るわけではありませんが、概念的には SocketAddress
が表現するようなアドレス情報(ポート、ホスト、ファミリー)を引数として受け取ります。SocketAddress
が提供する情報は、このようなサーバーの設定に役立ちます。
注意: net.Server
の listen()
は、直接 new net.SocketAddress()
オブジェクトを引数として受け取らない点に注意してください。しかし、ここで定義されるアドレス情報(ポート、アドレス、ファミリー)は SocketAddress
が持つ情報と一致します。
const net = require('node:net');
// サーバーのポートとホストを定義
const PORT = 3000;
const HOST = '127.0.0.1'; // IPv4 のループバックアドレス
// net.SocketAddress オブジェクトでアドレス情報を表現
const serverAddressInfo = new net.SocketAddress({
address: HOST,
port: PORT,
family: 'ipv4'
});
// サーバーを作成
const server = net.createServer((socket) => {
console.log(`クライアントが接続しました: ${socket.remoteAddress}:${socket.remotePort}`);
socket.write('Hello from server!\r\n');
socket.on('end', () => {
console.log('クライアントが切断しました。');
});
});
// サーバーをリッスン開始
// listen() メソッドは、SocketAddress オブジェクトそのものではなく、
// オプションとしてポート、ホストなどを個別に受け取ります。
server.listen(serverAddressInfo.port, serverAddressInfo.address, () => {
console.log(`サーバーは ${serverAddressInfo.address}:${serverAddressInfo.port} でリッスン中です。`);
});
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`エラー: ポート ${serverAddressInfo.port} は既に使用されています。`);
} else {
console.error('サーバーエラー:', err.message);
}
});
// サーバーを停止する例(5秒後に停止)
setTimeout(() => {
server.close(() => {
console.log('サーバーが停止しました。');
});
}, 5000);
解説:
この例では、net.SocketAddress
オブジェクトでサーバーがリッスンするアドレス情報を定義し、その情報を server.listen()
メソッドの引数として利用しています。これにより、アドレス関連の設定を一箇所に集約し、可読性を高めることができます。
IPアドレスとポート番号を個別の引数として渡す方法
これは new net.SocketAddress()
が導入される以前から広く使われてきた、最も一般的な方法です。net.Server
の listen()
や net.Socket
の connect()
メソッドなどは、現在でもこの形式をサポートしています。
例: net.Server
でリッスンする場合
const net = require('node:net');
const PORT = 3000;
const HOST = '127.0.0.1'; // IPv4アドレス
const server = net.createServer((socket) => {
console.log(`クライアントが接続しました: ${socket.remoteAddress}:${socket.remotePort}`);
socket.end('Hello from server (using individual args)!\r\n');
});
// listen() メソッドにポートとホストを個別の引数として渡す
server.listen(PORT, HOST, () => {
console.log(`サーバーは ${HOST}:${PORT} でリッスン中です。`);
});
server.on('error', (err) => {
console.error('サーバーエラー:', err.message);
});
解説:
listen()
メソッドは、最初の引数にポート番号(数値)、2番目の引数にホスト名またはIPアドレス(文字列)を受け取ります。これが最も直感的で、簡単なサーバーやクライアントをセットアップする際に頻繁に利用されます。
利点:
- 既存の多くのAPIでサポートされている。
- シンプルで分かりやすい。
欠点:
family
(IPv4/IPv6)などの追加情報を明示的に渡す必要がある場合がある。
オプションオブジェクトにプロパティとして渡す方法
net.Server
の listen()
や net.Socket
の connect()
など、多くの net
モジュールのメソッドは、ポート、ホスト、ファミリーなどの情報を単一のオプションオブジェクトとして受け取ることもできます。これは new net.SocketAddress()
と似ていますが、net.SocketAddress
オブジェクトを直接渡すわけではありません。
例: net.Socket
で接続する場合
const net = require('node:net');
const PORT = 3000;
const HOST = '127.0.0.1'; // サーバーと同じIPv4アドレス
// 接続先のアドレス情報をオプションオブジェクトとして定義
const connectOptions = {
port: PORT,
host: HOST, // 'address' ではなく 'host' を使用
family: 'IPv4' // 'IPv4' または 'IPv6'
};
const client = new net.Socket();
// connect() メソッドにオプションオブジェクトを渡す
client.connect(connectOptions, () => {
console.log(`サーバーに接続しました: ${connectOptions.host}:${connectOptions.port}`);
client.write('Hello from client (using options object)!\r\n');
});
client.on('data', (data) => {
console.log('サーバーからのデータ:', data.toString());
client.destroy(); // 接続を終了
});
client.on('close', () => {
console.log('接続がクローズされました。');
});
client.on('error', (err) => {
console.error('クライアントエラー:', err.message);
});
利点:
new net.SocketAddress()
と概念的に近い。- 引数の順序を気にする必要がない。
欠点:
net.BlockList
のaddAddress()
やcheck()
など、特定のAPIではnet.SocketAddress
オブジェクトが期待される場合がある。net.SocketAddress
のように特定の型のオブジェクトではないため、型チェックなどの恩恵を受けられない。
Unixドメインソケットのパスを渡す方法
TCP/IPソケットではなく、ローカルマシン上のプロセス間通信(IPC)にUnixドメインソケットを使用する場合、アドレスはIPアドレスとポートではなく、ファイルシステム上のパスで指定されます。
例: Unixドメインソケットサーバーとクライアント
const net = require('node:net');
const fs = require('node:fs'); // ファイルシステムモジュール
const UNIX_SOCKET_PATH = '/tmp/my_unix_socket.sock'; // ソケットファイルのパス
// サーバー
const server = net.createServer((socket) => {
console.log('Unixドメインソケットクライアントが接続しました。');
socket.write('Hello from Unix server!\r\n');
socket.on('end', () => {
console.log('Unixクライアントが切断しました。');
});
});
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
// 以前のソケットファイルが残っている場合
console.warn(`ソケットファイル ${UNIX_SOCKET_PATH} が既に使用されています。削除して再試行します。`);
fs.unlinkSync(UNIX_SOCKET_PATH);
server.listen(UNIX_SOCKET_PATH);
} else {
console.error('Unixサーバーエラー:', err.message);
}
});
// ソケットファイルが存在する場合は削除してからリッスン
if (fs.existsSync(UNIX_SOCKET_PATH)) {
fs.unlinkSync(UNIX_SOCKET_PATH);
}
server.listen(UNIX_SOCKET_PATH, () => {
console.log(`Unixドメインソケットサーバーは ${UNIX_SOCKET_PATH} でリッスン中です。`);
});
// クライアント (少し遅れて接続)
setTimeout(() => {
const client = net.createConnection(UNIX_SOCKET_PATH, () => {
console.log('Unixドメインソケットサーバーに接続しました。');
client.write('Hello from Unix client!\r\n');
});
client.on('data', (data) => {
console.log('Unixサーバーからのデータ:', data.toString());
client.destroy();
});
client.on('close', () => {
console.log('Unixクライアント接続がクローズされました。');
});
client.on('error', (err) => {
console.error('Unixクライアントエラー:', err.message);
});
}, 1000);
解説:
Unixドメインソケットは、TCP/IPソケットとは異なり、ローカルファイルパスで識別されます。この場合、new net.SocketAddress()
は直接関係せず、パス文字列を直接 listen()
や createConnection()
に渡します。
利点:
- ネットワークスタックを経由しないため、オーバーヘッドが少ない。
- ローカルマシン上でのプロセス間通信に非常に効率的。
欠点:
- ソケットファイルが存在すると
EADDRINUSE
エラーが発生する場合があるため、適切なファイルクリーンアップが必要。 - 異なるホスト間での通信には使用できない。
new net.SocketAddress()
は、特に以下のような場合にその真価を発揮します。
- 厳密なアドレス情報のカプセル化: IPアドレス、ポート、ファミリー(IPv4/IPv6)、フローラベルといった情報を単一のオブジェクトとして厳密に管理したい場合。
- APIの統一性:
net.BlockList
のように、net.SocketAddress
オブジェクトを引数として明示的に期待するAPIを使用する場合。 - 複雑なネットワーク設定: 複数のネットワークインターフェースや異なるIPファミリーを扱うような、より複雑なネットワークアプリケーションで、アドレス情報を一貫して管理したい場合。