友達に「Socket.IOはメモリリークヤバイから、新規開発なら迷わずEngine.IOを使え」と言われたのでEngine.IOを直接さわってみた。
Engine.IOはSocket.IOの内部で使われている部品で、「websocketでもajaxでも何でもいいのでとにかく通信ができる状態」を作るライブラリ。
もともとSinatra::RocketIOlinda-socket.ioRuby用socket.ioクライアントを作る時に中身は覗いてたけど単体で使った事はなかったし、どういう実装か忘れがちなのでメモする。

Chatを作った

とりあえずchatを作った。Herokuでwebsocketを動かしている。
サーバークライアントも50行ぐらいで書けた。



感想としては、Engine.io単体で使うと、綺麗なアプリケーションを書くにはその上に(フル機能ではないにしても)Socket.IO的な物を実装しないと無理だなと思った。やっぱり通信がしたいのではなくて、メッセージングがしたい。

なお似たような実装でSocket.IOでのチャットも以前作った。ひと通りの機能や挙動を確認するのにはチャットを作るのがよいと思う。


Engine.IOに無い機能(Socket.IOが実装してる機能)

Engine.IOはSocket.IOの後継、ではなく内部部品で、wsというwebsocket npmをラップしつつhttpからのupgrade機能とxhr-pollingやjsonpでのフォールバックを付けたライブラリ。
生のwsを直接使ってるのに近いので、バイナリのやりとりはしやすそうな気がする。

低レイヤーな分、気の利いた機能はない。そういうのはSocket.IOが担当している。

クライアントライブラリをpathに追加してくれる機能

自分でengine.io-clientからengine.io.jsを持ってきて、配置する必要がある。

Socket.IOでは/socket.io/socket.io.jsとかがサーバーのpathに現れてくれる。
ちなみにこの部分、connectのリクエストを横取りするためにEventEmitterが埋め込んでるコールバックのevents配列を直接いじっててすごい事になっている。


websocket切断時の自動再接続

websocketが切断されても、クライアントライブラリは自動再接続してくれない。on(‘close’)などのイベントで自分で接続し直す必要がある。

Socket.IOでは特に何も考えなくてもクライアントは再接続してくれる。サーバー側は60秒毎に全クライアントの生存確認を行っていて、そのためにクライアントは25秒ごとにheartbeatを送っている。

オブジェクトの自動シリアライズ・デシリアライズ

Socket.IOではオブジェクトをそのまま送りあえるけど、Engine.IOはsend(string or buffer)しかないのでJSONやmsgpackで自分でシリアライズ・デシリアライズしろという実装になっている。
JSONじゃない物が来た時とか、try catchして〜とやってると一気にコードが長くなってつらい。

リモートのイベントをdispatchする機能

emit(イベント名, データ)でリモートのイベントを発火させられない。
send(string or buffer)とon(‘message’, callback(string)) しかないので、受け取ったオブジェクトを自分でJSON.parseするなどして扱わなければならない。
EventEmitterとか使わないと巨大なswitch文が生まれるし、まあこの辺を自動的にやってくれてたのが、ものすごくコード量を減らしてくれてた事がわかる。

クライアント全員へのブロードキャスト

sockets.emitみたいなのは無いので、
for client_id, client of engine.clients
client.send JSON.stringify {type: "chat", message: "ハロー"}
のように自分でclients全員に送らなければならない

rooms機能が無い

クライアントをグループ分けするrooms機能は無い。
engine.io-roomsのようなプラグインを使う必要がある。


Engine.IOにある機能

on “packet”や”packetCreate”などの低レベルなイベントがあるので、デバッグに便利そう