Socket.ioを思いっきり勘違いしていた


ひょんな拍子にSocket.ioを使い始めたのですが、根っこで勘違いをしていたために、どうにも理解できないことだらけでいましたが、根っこがわかればほとんど氷解しました。

TL; DR

  • Socket.ioは(論理上も)クライアント-サーバ型
  • 上りの通信相手はサーバのみ、下りの相手は適宜選択
  • イベントは送った先でしか起きない

(いちおうの注記ですが、クライアント→サーバの通信を「上り」、逆を「下り」としています)

全体の枠組み

Socket.ioの応用例としていちばんメジャーなのがリアルタイムチャットということもあって、(もちろん物理的にクライアントとサーバがある構成だというのは明らかなのですが)論理的にはクライアント同士が直接やり取りしているものだと誤解していました。

実際には、Socket.ioの論理上も、クライアントはSocket.ioサーバと1対1で接続していて、サーバ側では接続を複数本持っている、というような構成です。クライアント同士がサーバそっちのけで通信しだす、というシステムでは全くありません。

通信のときに何が起きるのか

非同期の通信、そしてNode.js環境ということもあって、Socket.ioはイベントドリブンな構成となっています。

上りの通信

クライアントから送信する場合、クライアントがつながるのはサーバだけですので、emit1する先の選択肢はもちろんありません。そして、クライアントからemitすると、サーバ側で対応したイベントが発生します。

下りの通信

サーバ側からはすべての接続が見えますので、送信先を選ぶことができます。「全接続」「Namespace単位」「room単位」と、それぞれにemitできます。サーバ側同様、届いたクライアントでは対応するイベントが発生します。

PHPからemitする手段

PHPとSocket.ioを連携させる場合に、PHP側のライブラリも複数ありますが、発想が違うことがあるので注意が必要です。

elephant.io

elephant.ioは、Socket.ioに対してクライアントとして働きます。つまり、emitする先はサーバの一択です。また、emitのたびに「Socket.ioサーバとのコネクションを確立→emit→切断」のような動きをしますので、かなり重いです。

socket.io-php-emitter

Socket.ioには、Redisにあるpub/sub機能と連携するという設定があります。こうすることで、多数のSocket.ioサーバで負荷分散、のようなこともできるようになるのですが、このRedis経由でemitを行うのがsocket.io-php-emitterです。サーバサイドのSocket.ioと同様に動作しますので、emit先はクライアントになります(もちろん、誰に送るかも設定可能です)。


  1. 「イベントを起こす」ことに、fireやtriggerなどいろいろな動詞を使いますが、EventEmitterのあるNode.js界ではemitがよく使われるようです。