Node.js net.createServer() デバッグテクニック:エラー解決とパフォーマンス改善

2025-04-26

以下に、net.createServer()の基本的な使い方と説明をします。

net.createServer()の基本的な使い方

const net = require('net');

const server = net.createServer((socket) => {
  // クライアントからの接続があったときに実行されるコード
  console.log('クライアントが接続しました。');

  // クライアントからのデータを受信する
  socket.on('data', (data) => {
    console.log('クライアントからのデータ:', data.toString());
    //クライアントへデータを送り返す
    socket.write('サーバーから応答: ' + data);
  });

  // クライアントとの接続が切断されたときに実行されるコード
  socket.on('end', () => {
    console.log('クライアントが切断しました。');
  });

  // クライアントとの接続でエラーが発生したときに実行されるコード
  socket.on('error', (err) => {
    console.error('クライアントとの接続でエラー:', err);
  });

});

// 指定されたポートでサーバーを起動する
server.listen(3000, () => {
  console.log('サーバーがポート3000で起動しました。');
});
  1. require('net'): netモジュールを読み込みます。このモジュールには、TCPサーバーを作成するための機能が含まれています。
  2. net.createServer((socket) => { ... }): net.createServer()関数を呼び出し、TCPサーバーを作成します。この関数には、クライアントが接続されたときに実行されるコールバック関数を引数として渡します。
    • socket: コールバック関数に渡されるsocketオブジェクトは、クライアントとの接続を表します。このオブジェクトを使用して、データの送受信や接続の制御を行います。
    • socket.on('data', (data) => { ... }): socketオブジェクトのdataイベントをリッスンし、クライアントからデータを受信したときに実行されるコールバック関数を登録します。data引数には、クライアントから送信されたデータがBufferオブジェクトとして渡されます。
    • socket.on('end', () => { ... }): socketオブジェクトのendイベントをリッスンし、クライアントとの接続が切断されたときに実行されるコールバック関数を登録します。
    • socket.on('error', (err) => { ... }): socketオブジェクトのerrorイベントをリッスンし、クライアントとの接続でエラーが発生したときに実行されるコールバック関数を登録します。err引数には、エラー情報が渡されます。
    • socket.write('サーバーから応答: ' + data):クライアントへデータを送信します。
  3. server.listen(3000, () => { ... }): server.listen()関数を呼び出し、指定されたポート(例:3000)でサーバーを起動します。この関数には、サーバーが起動したときに実行されるコールバック関数を引数として渡します。


一般的なエラーとトラブルシューティング

  1. ポートの競合 (Address already in use: bind)
    • エラー内容: Error: listen EADDRINUSE: address already in use :::3000 のようなエラーメッセージが表示されます。
    • 原因: 指定されたポートが既に別のプロセスで使用されている場合に発生します。
    • トラブルシューティング:
      • 別のポート番号を使用する。
      • netstat (Windows) または lsof (macOS/Linux) コマンドを使用して、どのプロセスがポートを使用しているかを確認し、そのプロセスを終了する。
      • サーバーを再起動する前に、以前のサーバープロセスが完全に終了していることを確認する。
  2. ファイアウォールによる接続拒否 (Connection refused)
    • エラー内容: クライアントからサーバーに接続しようとすると、ECONNREFUSED エラーが発生します。
    • 原因: ファイアウォールがサーバーへの接続をブロックしている場合に発生します。
    • トラブルシューティング:
      • サーバーが使用しているポートをファイアウォールで許可する設定を追加する。
      • ネットワーク環境を確認し、ルーターやプロキシの設定を確認する。
  3. サーバーが起動しない (Server not listening)
    • エラー内容: server.listen() を呼び出しても、サーバーが起動しない。
    • 原因: server.listen() のコールバック関数が実行されていない、またはエラーが発生している可能性があります。
    • トラブルシューティング:
      • server.listen() のコールバック関数内に console.log() を追加して、関数が実行されているか確認する。
      • try catch文などでエラーハンドリングを行い、エラーが発生しているかを確認する。
      • ポート番号が正しいか確認する。
  4. クライアントからのデータ受信エラー (Data event not firing)
    • エラー内容: socket.on('data', ...) で登録したコールバック関数が実行されない。
    • 原因: クライアントがデータを送信していない、またはデータの形式が正しくない可能性があります。
    • トラブルシューティング:
      • クライアントがデータを送信しているか確認する。
      • クライアントとサーバーでデータのエンコーディングが一致しているか確認する (例: UTF-8)。
      • クライアントから送信されるデータの形式を確認し、サーバー側で適切な処理を行う。
  5. 接続の切断 (Connection reset by peer)
    • エラー内容: ECONNRESET エラーが発生し、接続が切断される。
    • 原因: クライアントまたはサーバーが予期せず接続を終了した場合に発生します。
    • トラブルシューティング:
      • クライアントとサーバーのログを確認し、接続が切断された原因を特定する。
      • ネットワーク環境を確認し、接続が不安定になっていないか確認する。
      • クライアント、サーバー両方のタイムアウト設定を確認する。
  6. socketのエラーハンドリング不足
    • エラー内容: socketのエラーをハンドリングしていないため、エラーが発生した場合プログラムが止まってしまう。
    • トラブルシューティング:
      • socket.on('error', (err)=>{console.error(err)})を必ず実装する。
  7. サーバーの閉じ忘れ
    • エラー内容: サーバーを閉じない限り、プロセスが終了しない。
    • トラブルシューティング:
      • server.close()を実装し、サーバーを閉じるようにする。
  • Node.jsのデバッガーを使用する。
  • ネットワーク監視ツール (例: Wireshark) を使用して、ネットワークトラフィックを分析する。
  • エラーメッセージをよく読み、原因を特定する。
  • console.log() を使用して、変数の値や実行パスを確認する。


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.listen(3000, ...) でサーバーをポート3000で起動します。
  • クライアント側の接続でエラーが発生した場合、socket.on('error', ...)イベントがトリガーされ、エラー内容が表示されます。
  • クライアントが切断されると、socket.on('end', ...) イベントがトリガーされ、console.log('クライアントが切断しました。') が表示されます。
  • クライアントからデータが送信されると、socket.on('data', ...) イベントがトリガーされ、console.log('クライアントからのデータ:', data.toString()) でデータが表示され、socket.write(...) でクライアントに応答が送信されます。
  • クライアントが接続されると、console.log('クライアントが接続しました。') が表示されます。
  • このコードは、ポート3000でTCPサーバーを起動し、クライアントからの接続を待ち受けます。
const net = require('net');

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

  socket.on('data', (data) => {
    console.log(`クライアント${socket.remoteAddress}:${socket.remotePort}からのデータ:`, data.toString());
    socket.write(`サーバー(${socket.remoteAddress}:${socket.remotePort})からの応答: ${data.toString()}`);
  });

  socket.on('end', () => {
    console.log(`クライアント${socket.remoteAddress}:${socket.remotePort}が切断しました。`);
  });

  socket.on('error', (err) => {
    console.error(`クライアント${socket.remoteAddress}:${socket.remotePort}接続エラー:`, err);
  });
});

server.listen(3000, () => {
  console.log('サーバーがポート3000で起動しました。');
});

説明

  • クライアントに応答を送る際にも、クライアントのIPアドレスとポート番号を含めています。
  • これにより、どのクライアントからのデータであるかを区別できます。
  • socket.remoteAddresssocket.remotePort を使用して、接続したクライアントのIPアドレスとポート番号を取得し、ログに表示します。
  • このコードは、複数のクライアントからの接続を処理できるように拡張されています。
const net = require('net');

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

  let buffer = '';

  socket.on('data', (data) => {
    buffer += data.toString();
    while (buffer.includes('\n')) {
      const line = buffer.substring(0, buffer.indexOf('\n'));
      buffer = buffer.substring(buffer.indexOf('\n') + 1);
      console.log('受信:', line);
      socket.write('サーバー受信:' + line + '\n');
    }
  });

  socket.on('end', () => {
    console.log('クライアントが切断しました。');
  });

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

server.listen(3000, () => {
  console.log('サーバーがポート3000で起動しました。');
});
  • これにより、複数のメッセージが一度に送信された場合でも、各メッセージを個別に処理できます。
  • buffer 変数に受信したデータを追加し、改行文字が見つかるたびにデータを処理します。
  • このコードは、クライアントから送信されるデータが改行文字(\n)で区切られていることを前提としています。


Socket.io (WebSocket)

  • 欠点
    • TCPよりもオーバーヘッドが大きい。
    • TCPの低レベルな制御が必要な場合には不向き。
  • 利点
    • リアルタイム通信に最適。
    • クロスプラットフォームに対応。
    • 高レベルなAPIで開発が容易。
  • 使用例
    const http = require('http');
    const socketIo = require('socket.io');
    
    const server = http.createServer();
    const io = socketIo(server);
    
    io.on('connection', (socket) => {
      console.log('クライアントが接続しました。');
    
      socket.on('message', (data) => {
        console.log('クライアントからのメッセージ:', data);
        io.emit('message', `サーバーからの応答: ${data}`);
      });
    
      socket.on('disconnect', () => {
        console.log('クライアントが切断しました。');
      });
    });
    
    server.listen(3000, () => {
      console.log('Socket.ioサーバーがポート3000で起動しました。');
    });
    
  • 説明
    • WebSocketプロトコルを使用して、双方向のリアルタイム通信を実現します。
    • TCPよりも高レベルな抽象化を提供し、ブラウザとの通信に最適化されています。
    • チャットアプリケーション、リアルタイムゲーム、ダッシュボードなどのリアルタイムアプリケーションに適しています。

Express.js (HTTP/HTTPS)

  • 使用例
  • 説明
    • HTTP/HTTPSサーバーを作成するためのフレームワークです。
    • REST APIやWebアプリケーションの構築に適しています。
    • TCPソケットを直接操作するよりも、HTTPリクエスト/レスポンスを扱うことに特化しています。

const express = require('express'); const app = express();

app.get('/', (req, res) => {
  res.send('Hello, Express!');
});

app.listen(3000, () => {
  console.log('Expressサーバーがポート3000で起動しました。');
});
```
  • 欠点
    • TCPソケットの直接制御が必要な場合には不向き。
    • リアルタイム通信にはWebSocketなどの追加手段が必要。
  • 利点
    • HTTP/HTTPSサーバーの開発が容易。
    • 豊富なミドルウェアが利用可能。
    • REST APIの構築に最適。

dgram (UDP)

  • 欠点
    • 信頼性が低い (データが失われる可能性がある)。
    • データの順序が保証されない。
  • 利点
    • 高速な通信が可能。
    • ブロードキャストやマルチキャストに対応。
  • 使用例
    const dgram = require('dgram');
    const socket = dgram.createSocket('udp4');
    
    socket.on('message', (msg, rinfo) => {
      console.log(`クライアントからのメッセージ: ${msg} from <span class="math-inline">\{rinfo\.address\}\:</span>{rinfo.port}`);
      socket.send(`サーバーからの応答: ${msg}`, rinfo.port, rinfo.address);
    });
    
    socket.bind(3000, () => {
      console.log('UDPサーバーがポート3000で起動しました。');
    });
    
  • 説明
    • UDP (User Datagram Protocol) を使用して、データグラムソケットを作成します。
    • TCPとは異なり、コネクションレスで高速な通信を実現します。
    • リアルタイムゲーム、ビデオストリーミング、DNSなどのアプリケーションに適しています。
  • 欠点
    • 複雑な設定が必要。
    • プロセス間通信のオーバーヘッド。
  • 利点
    • パフォーマンスの向上。
    • 耐障害性の向上。
  • 使用例
    const cluster = require('cluster');
    const net = require('net');
    const os = require('os');
    
    if (cluster.isMaster) {
      const numCPUs = os.cpus().length;
      for (let i = 0; i < numCPUs; i++) {
        cluster.fork();
      }
    } else {
      net.createServer((socket) => {
        // ...
      }).listen(3000);
    }
    
  • 説明
    • Node.jsのマルチプロセス機能を使用して、複数のワーカープロセスを起動し、負荷分散を行います。
    • net.createServer() と組み合わせて使用することで、高負荷なTCPサーバーを構築できます。