Node.js net.Serverで学ぶネットワークプログラミング:基礎から応用まで
2025-04-26
net.Serverの主な役割
- 接続管理
接続されたクライアントの管理や、接続の終了処理を行います。 - イベント処理
接続、データ受信、エラーなどのイベントを処理するためのイベントリスナーを登録できます。 - 接続の受付
クライアントからの接続要求を待ち受け、接続が確立されると、新しいnet.Socket
オブジェクトを生成します。
new net.Server() の使い方
new net.Server()
は、net.Server
クラスのインスタンスを生成します。このインスタンスは、サーバーの動作を制御するためのメソッドやプロパティを持っています。
基本的な使用方法は以下の通りです。
-
const net = require('net');
-
net.Serverインスタンスを生成する
const server = net.createServer();
-
イベントリスナーを登録する
'connection'
イベント: クライアントが接続されたときに発生します。'listening'
イベント: サーバーが指定されたポートで接続を待ち受け始めたときに発生します。'error'
イベント: サーバーでエラーが発生したときに発生します。'close'
イベント: サーバーが閉じた時に発生します。
server.on('connection', (socket) => { console.log('クライアントが接続しました。'); socket.on('data', (data) => { console.log('クライアントからデータを受信しました:', data.toString()); socket.write('サーバーからの応答: ' + data.toString()); }); socket.on('end', () => { console.log('クライアントが切断しました。'); }); }); server.on('listening', () => { console.log('サーバーがポート3000で接続を待ち受け始めました。'); }); server.on('error', (err) => { console.error('サーバーエラー:', err); });
-
サーバーを起動する
server.listen(3000, () => { console.log('サーバー起動'); });
例の解説
server.listen(3000, ...)
で、サーバーをポート3000で起動します。socket.on('end', ...)
で、クライアントが切断したときの処理を定義します。socket.on('data', ...)
で、クライアントからデータを受信したときの処理を定義します。server.on('connection', ...)
で、クライアントが接続されたときの処理を定義します。接続されたクライアントとの通信は、socket
オブジェクトを通じて行います。net.createServer()
でサーバーインスタンスを作成します。
一般的なエラーとトラブルシューティング
-
- エラー
Error: listen EADDRINUSE: address already in use :::3000
のように、EADDRINUSE
エラーが発生する。 - 原因
指定したポートがすでに別のプロセスで使用されている。 - 解決策
- 別のポートを使用する。
netstat
(Windows) またはlsof
(macOS/Linux) コマンドを使用して、どのプロセスがポートを使用しているかを確認し、必要に応じてそのプロセスを終了する。- サーバーを終了した後に、すぐに再起動しようとすると、OSがポートを解放するのに時間がかかる場合があります。少し待ってから再起動してみてください。
- エラー
-
ファイアウォールによる接続拒否 (Firewall Blocking)
- エラー
クライアントがサーバーに接続できない。 - 原因
ファイアウォールがサーバーのポートへの接続をブロックしている。 - 解決策
- ファイアウォール設定で、サーバーのポートへの接続を許可するルールを追加する。
- 一時的にファイアウォールを無効にして、問題がファイアウォールに関連しているかどうかを確認する。
- エラー
-
ネットワークインターフェースの問題 (Network Interface Issues)
- エラー
Error: listen EADDRNOTAVAIL
のように、EADDRNOTAVAIL
エラーが発生する。 - 原因
指定したIPアドレスが利用できない、またはネットワークインターフェースがダウンしている。 - 解決策
server.listen()
の第二引数に、利用可能なIPアドレスを指定する。server.listen(3000, '127.0.0.1')
のように。0.0.0.0
を使用して、すべての利用可能なインターフェースでリッスンする。- ネットワーク接続を確認し、必要に応じて再起動する。
- エラー
-
connection イベントが発生しない
- エラー
クライアントが接続しても、connection
イベントが発火しない。 - 原因
- サーバーが正常に起動していない。
- クライアントが間違ったポートまたはIPアドレスに接続しようとしている。
- ネットワークの問題で接続がタイムアウトしている。
- 解決策
listening
イベントが正常に発火していることを確認する。- クライアントの接続先を確認する。
- ネットワーク接続を確認する。
- サーバーのログを確認し、エラーメッセージがないか確認する。
- エラー
-
data イベントでデータが受信されない
- エラー
クライアントからデータを送信しても、data
イベントが発火しない。 - 原因
- クライアントがデータを送信していない。
- データの送信方法が間違っている。
- ネットワークの問題でデータが損失している。
- 解決策
- クライアントがデータを送信していることを確認する。
- クライアントとサーバーのデータ形式が一致していることを確認する。
- ネットワーク接続を確認する。
- エラー
-
サーバーの終了処理
- エラー
サーバーが正常に終了しない。 - 原因
server.close()
を呼び出していない。- 接続中のクライアントが残っている。
- 解決策
server.close()
を呼び出してサーバーを終了する。- 接続中のクライアントをすべて切断してからサーバーを終了する。
server.unref()
を使用して、Node.js のイベントループがサーバーの終了を待たないようにする。
- エラー
-
エラーハンドリングの欠如
- エラー
サーバーが予期しないエラーでクラッシュする。 - 原因
error
イベントのハンドリングが不十分。 - 解決策
error
イベントのリスナーを追加し、エラーを適切に処理する。
- エラー
デバッグのヒント
- ネットワークモニタリングツール (Wireshark など) を使用して、ネットワークトラフィックを監視する。
- Node.js のデバッガーを使用する。
try...catch
ブロックを使用して、エラーを捕捉する。console.log()
を使用して、サーバーの状態や変数の値を出力する。
基本的なTCPサーバーの例
const net = require('net');
const server = net.createServer((socket) => {
console.log('クライアントが接続しました。');
// クライアントからのデータを受信する
socket.on('data', (data) => {
console.log('クライアントからのデータ:', data.toString());
// クライアントに応答を送信する
socket.write('サーバーからの応答: ' + data.toString());
});
// クライアントが切断したときの処理
socket.on('end', () => {
console.log('クライアントが切断しました。');
});
// エラーハンドリング
socket.on('error', (err) => {
console.error('ソケットエラー:', err);
});
});
// サーバーをポート3000で起動する
server.listen(3000, () => {
console.log('サーバーがポート3000で起動しました。');
});
// サーバーのエラーハンドリング
server.on('error', (err) => {
console.error('サーバーエラー:', err);
});
説明
net.createServer()
でサーバーインスタンスを作成し、接続されたクライアントのソケットを引数とするコールバック関数を渡します。socket.on('data', ...)
で、クライアントからデータを受信したときの処理を定義します。受信したデータをtoString()
で文字列に変換し、コンソールに出力します。socket.write(...)
で、クライアントに応答を送信します。socket.on('end', ...)
で、クライアントが切断したときの処理を定義します。socket.on('error', ...)
で、ソケットエラーの処理を定義します。server.listen(3000, ...)
で、サーバーをポート3000で起動します。server.on('error', ...)
で、サーバー全体のエラーハンドリングを定義します。
複数のクライアントを扱う例
const net = require('net');
const server = net.createServer((socket) => {
console.log('クライアントが接続しました。');
socket.on('data', (data) => {
console.log('クライアントからのデータ:', data.toString());
socket.write('サーバーからの応答: ' + data.toString());
});
socket.on('end', () => {
console.log('クライアントが切断しました。');
});
socket.on('error', (err) => {
console.error('ソケットエラー:', err);
});
});
server.listen(3000, () => {
console.log('サーバーがポート3000で起動しました。');
});
server.on('error', (err) => {
console.error('サーバーエラー:', err);
});
この例は、複数のクライアントが同時に接続しても、それぞれのクライアントとの通信を処理できます。Node.jsは非同期処理が得意なので、複数のクライアントの接続を効率的に処理できます。
特定のIPアドレスでリッスンする例
const net = require('net');
const server = net.createServer((socket) => {
console.log('クライアントが接続しました。');
socket.on('data', (data) => {
console.log('クライアントからのデータ:', data.toString());
socket.write('サーバーからの応答: ' + data.toString());
});
socket.on('end', () => {
console.log('クライアントが切断しました。');
});
socket.on('error', (err) => {
console.error('ソケットエラー:', err);
});
});
server.listen(3000, '127.0.0.1', () => { // ローカルホストでのみリッスン
console.log('サーバーがポート3000で起動しました。');
});
server.on('error', (err) => {
console.error('サーバーエラー:', err);
});
この例では、server.listen()
の第二引数に '127.0.0.1'
を指定することで、ローカルホストからの接続のみを受け付けるようにしています。
const net = require('net');
const server = net.createServer((socket) => {
// ... クライアントとの通信処理 ...
});
server.listen(3000, () => {
console.log('サーバーがポート3000で起動しました。');
});
// サーバーを終了する
server.close(() => {
console.log('サーバーを終了しました。');
});
Express.js + Socket.IO (WebSocket)
- 利点
- 双方向通信が可能。
- リアルタイム性が高い。
- ブラウザとの互換性が高い。
- HTTPサーバーと統合しやすい。
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = socketIO(server);
io.on('connection', (socket) => {
console.log('クライアントが接続しました。');
socket.on('chat message', (msg) => {
io.emit('chat message', msg); // 全てのクライアントに送信
});
socket.on('disconnect', () => {
console.log('クライアントが切断しました。');
});
});
server.listen(3000, () => {
console.log('サーバーがポート3000で起動しました。');
});
UDP (dgramモジュール)
- 利点
- 高速な通信が可能。
- ブロードキャストやマルチキャストが可能。
const dgram = require('dgram');
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`サーバーが ${rinfo.address}:${rinfo.port} からメッセージを受信しました: ${msg}`);
server.send(`サーバーからの応答: ${msg}`, rinfo.port, rinfo.address, (err) => {
if (err) {
console.error(err);
}
});
});
server.on('listening', () => {
const address = server.address();
console.log(`サーバーが ${address.address}:${address.port} でリッスンしています。`);
});
server.bind(3000);
gRPC
- 注意
gRPCを使うには、プロトコルバッファの定義が必要であり、少し複雑になる場合があります。 - 利点
- 高性能な通信。
- 言語中立性。
- ストリーミング通信が可能。
- 利点
- パフォーマンスの向上。
- 多重化により、複数のリクエストを同時に処理できる。
const http2 = require('http2');
const fs = require('fs');
const server = http2.createSecureServer({
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt')
}, (stream, headers) => {
stream.respond({
'content-type': 'text/html',
':status': 200
});
stream.end('<h1>Hello World</h1>');
});
server.listen(3000, () => {
console.log('HTTP/2サーバーがポート3000で起動しました。');
});