LoginSignup
1
0

More than 1 year has passed since last update.

npm ws のクライアントインスタンス生成,接続時イベントの登録,接続手続きの順番

Last updated at Posted at 2021-04-30

node.jsで簡単にwebsocketの機能をつかえるようになるライブラリws
つかっていて,サーバーの方はまあわかるとして,クライアントClass: Websocketの挙動がというか,設計がわかりにくかったので実験したらふぇぇって感じだった.
(とはいえまあサーバーの方で大量に管理するクライアントの記述を思うとこれでちょうどいいのかもしれない.)

気になった点

クライアントクラスの実行例として以下のようなモノがGitにある

client
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:0ws state:1との間にあったevent as connectedが表示されなくなっている.

といわけで,なんだか意図しないデータの取りこぼしとか起こりそうだなと思ってなんか気もち悪い気がしたというわけでした.

ちなみに,コンストラクタで接続を開始せずに,あとから接続を開始できる関数がないか探しましたが見当たりませんでした.というか検索していると再接続もできないので,別のを作ったり使ってる人が見受けられました.もし接続用の関数があれば再接続もできるはずなので,やはりないのでしょう.

ここまで書いて誰かすでにQiitaで記事書いてそう.だけどいい練習になったのでよし.

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0