Help us understand the problem. What is going on with this article?

StreamでBuffer.concatしない冴えたやりかた

はじめに

この記事は Node.js Advent Calendar 2019 の 24 日目です。

https://twitter.com/yosuke_furukawa/status/1201778011286065153
Yosuke FURUKAWA @yosuke_furukawa
なんだこの誕生日アドベントカレンダーは / “Node.js Advent Calendar 2019 - Qiita”
午後5:19 · 2019年12月3日·はてなブックマーク

最初は誕生日カレンダーみたいになってたので、ネタ記事を書こうかなーと思っていました。
ですが、始まってみると案外真面目な記事ばかりだったので、真面目に書くことにしました。
(そう、私は12/24生まれです!{クリスマス,誕生日}{ケーキ,プレゼント}はいつも一つ。)

StreamでBuffer.concatしない冴えたやりかた

一般的にStreamを扱う時は on("data", ...) で処理することが多いと思います。
しかし、実際に使うサイズと流れてくるサイズは一致しないことが多く…

const net = require('net');

function onRecv(packet){ /* ... */ }

const server = net.createServer(conn => {
    let receivedQueue = Buffer.alloc(0);
    conn.on('data', chunk => {
        let data = Buffer.concat([receivedQueue, chunk]);
        //40バイトずつ読み出したい
        while(data.length >= 40){
            onRecv(data.slice(0, 40));
            data = data.slice(40);
        }
        receivedQueue = data;
    });
}).listen(3000);

のような処理になりがちです。…なんだか冗長に感じませんか?

他の言語では読み出し可能バイト数を取得、指定バイト以上なら一気に読み出す…みたいな処理を書くことが多いと思います。Arduinoのシリアル通信とか。
Node.jsで同じような処理は書けないのか?書けます。

Node.jsには、任意のバイト数を読み出すための readable イベントが存在します。

しかし、以前までは "読み出し可能なバイト数" を取得する方法が
stream._readableState.length
しかありませんでした。_が気になりますね…
でも今は大丈夫!
v9.4.0から、これを取得するための readableLength プロパティが追加されています。
stream.readableLength
罪悪感(?)が無くなりましたね。

これを使うと、先程のコードを以下のように書くことができます:

const net = require('net');

function onRecv(packet){ /* ... */ }

const server = net.createServer(conn => {
    conn.on('readable', () => {
        while(conn.readableLength >= 40){
            onRecv(conn.read(40));
        }
    });
}).listen(3000);

ああ…気持ちいい!これが書きたかった。これが求めていたもの・・・
コードが綺麗になることは勿論、Buffer.concatはデータの複製を行うので、処理速度の改善も期待できます。
このパターンが使える場面では積極的にreadableイベントを使っていきましょう。

注意点

この方法で読み込める最大バイト数は highWaterMark までになります。
大きなデータをまとめて取得したい時は、 highWaterMark の上限値を拡大する必要があります。
※ただ、デフォルトの highWaterMark を超えるデータを一気に処理する処理自体あまり良くないので、少しずつ処理できるようにしたほうがいいかもしれません。

p.s. highWaterMark は、シンプルに説明すると他言語のbufferSizeみたいなものです。(実際にはもっとイケてる実装の一部だったりしますが、とりあえずはそんなものだと思えば十分。)

さいごに

あまりreadableイベントを使った記事を見かけないので書いてみました。お試しあれ。
おわりー。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした