matsutoba’s blog

フロントエンドエンジニアをしています

Node.jsでTCP通信

Node.jsのnetライブラリを使ったTCP通信プログラムの開発で、イベントの意味を間違えていたので覚書をしておきます。

バイナリデータの扱うときはnetライブラリではできないのかと思いましたが、ただの私の勘違いでした。

ちなみに初めは Socket.IO で実現できるのかと思いましたが、これはWebSocket用なので、純粋なTCP通信には利用できないようです。

勘違いしていたところ

通信時のイベントとして、connection, data, end, close などがありますが、dataとendの意味を間違えていました。

  • data
    • データを受信するごとに、このコールバックが実行される。
  • end
    • すべてのデータが受信されたときに、このコールバックが実行される。

実際は、一度にすべてのデータを受け取れるわけではなく、複数回に分けてdataイベントでデータを受信する場合がありました。

その後、データを受信しきるか、クライアント側が切断したときにendイベントが発生しました。
(発生しました、というか私が仕様を間違って理解しただけ。)

正しい扱いかた

データは複数回に分けて受信することがあるので、

  • dataイベントでは最後のデータが来るまで受信したデータを連結して保存しておく。
  • データを受信しおわるとendイベントが発生する。

データ終了を表すコードがある場合は、

  • dataイベントでチェックする。
  • 終了コードを受信した場合、クライアント側に返すコードがあれば送信。
  • 通信が終了するとendイベントが発生する。
'use strict';
let net = require('net');
const EOF = 0x04; //EOT
const ACK = 0x06; //ACK
const LISTEN_PORT = 9000;
const server = net.createServer();
let counter=0;
server.on('connection', (socket)=>{
    socket.setEncoding('utf8');
    counter++;
    console.log(`Client connected [${counter}] \n`);
    let data = '';
    socket.on('data', (chunk)=>{
        console.log(`Data [${counter}]: ${chunk.length}`);
        data += chunk;
        let eof = chunk[chunk.length-1].charCodeAt(0);
        if (eof===EOT) {
            console.log(`Send ACK  [${counter}]`);
            let buf = new Buffer.alloc(1);
            buf.writeInt8(ACK);
            socket.write(buf);    
        }
    });
    socket.on('end', ()=>{
        console.log(`End [${counter}]`);
        console.log(data);
    })
    socket.on('close', ()=>{
        console.log(`Close [${counter}]\n`);
    })
});
server.listen(LISTEN_PORT, ()=>{
    console.log('Server start.');
});