node.js
で簡単にwebsocketの機能をつかえるようになるライブラリws
を
つかっていて,サーバーの方はまあわかるとして,クライアントClass: Websocket
の挙動がというか,設計がわかりにくかったので実験したらふぇぇって感じだった.
(とはいえまあサーバーの方で大量に管理するクライアントの記述を思うとこれでちょうどいいのかもしれない.)
気になった点
クライアントクラスの実行例として以下のようなモノがGitにある
const WebSocket = require('ws');
const ws = new WebSocket('ws://www.host.com/path');
ws.on('open', function open() {
const array = new Float32Array(5);
for (var i = 0; i < array.length; ++i) {
array[i] = i / 2;
}
ws.send(array);
});
これだけである.簡単!と思えればいいのだけど,
素朴に考えると,接続完了時に発行される'open'
に対するEventを登録(ws.on()
)してから接続を開始する気がする.
しかしながらこの例には続きがなく,これで完結しており,実際別プロセスでws.Server
のサンプルプログラムを動かしてからこれを実行すると,ちゃんと動く.
.on()
は継承(?)しているEventなんとかクラスのものであって,Websocket
クラス固有のものではない.
したがって接続を開始するとしたらコンストラクタでしかありえない.
実際ソースを見てみると,そんな感じだった.
詳しくは見ておらず,間違っている可能性も大いにあるが,流れとしては
- クライアント: コンストラクタでいろいろ調整
- クライアント: おそらくhttpで
upgrade
が承認されたとき用のイベントを登録 - クライアント: httpでupgradeをhttpサーバーに要請
- ここでコンストラクタの処理が終わる.
- そしてhttpサーバーがwssocketサーバーにアップグレードした旨が伝わり次第,クライアントに
open
イベントが発行される.
という感じであった.
すなわち,ws.on()
はコンストラクタの処理が終わって,サーバーからupgrade
が通知されるまでの間に行われないと,'open'
イベントをキャッチできない.
実験
実際以下のようなプログラムで比較するとわかる.ちなみにサーバー側ではsend(data)
で送った内容(data
)がそっくりそのまま返ってくる.
const websocket = require("ws");
const sock = new websocket("ws://127.0.0.1:5001", {
perMessageDeflate: false
});
const openev = () => {
sock.on("open", () => {
console.log("event as connected ");
});
}
sock.on("message", data => {
console.log("event as getting message from the server");
console.log("echo: " + data);
});
sock.on("close", () => {
console.log("event as closed");
});
↑ここまで共有
Case 1:
console.log("ws state:" + sock.readyState);
openev(); //<-コンストラクタが終わってすぐくらいにopen event時のコールバック設定
//1s後にデータを送ってから接続を終える.
setTimeout(() => {
console.log("ws state:" + sock.readyState);
sock.send('a');
sock.close();
}, 1000);
結果:
ws state:0
event as connected
ws state:1
event as getting message from the server
echo: a
event as closed
case 2:
console.log("ws state:" + sock.readyState);
//openev();
//1s後にデータを送ってから接続を終える.
setTimeout(() => {
console.log("ws state:" + sock.readyState);
openev(); //<-1s後にopen event時のコールバック設定
sock.send('a');
sock.close();
}, 1000);
結果:
ws state:0
ws state:1
event as getting message from the server
echo: echo back: a
event as closed
case 1とcase 2を比較するとws state:0
とws state:1
との間にあったevent as connected
が表示されなくなっている.
といわけで,なんだか意図しないデータの取りこぼしとか起こりそうだなと思ってなんか気もち悪い気がしたというわけでした.
ちなみに,コンストラクタで接続を開始せずに,あとから接続を開始できる関数がないか探しましたが見当たりませんでした.というか検索していると再接続もできないので,別のを作ったり使ってる人が見受けられました.もし接続用の関数があれば再接続もできるはずなので,やはりないのでしょう.
ここまで書いて誰かすでにQiitaで記事書いてそう.だけどいい練習になったのでよし.