JavaScript
Node.js
Socket.io

Socket.IO 1.0の使い方と変更点

More than 5 years have passed since last update.

そろそろリリースされるかもしれない(?)開発中のSocket.IO 1.0を試したので、0.9からの変更点などをまとめます。ドキュメントがまともにないため主にソースを読んで調べています(つまり間違いがあるかも)。当然リリース時には機能が大きく変更となる可能性があります。

1.0のインストール方法

npmモジュールとしては提供されていないので、githubのmasterブランチをnpmコマンドでインストールします。

$ npm install LearnBoost/socket.io

これだけでサーバー側はだいたいOKのはずですが、クライアント側に依存ライブラリの設定などで不具合があるため、socket.io-clientのcomponent.jsonあたりをもろもろ調整します。とりあえず動かしたい場合は、forkして修正したバージョンをインストールしてください。

$ npm install nkzawa/sockt.io

基本部分のAPIはあまりかわってないので、サーバーの起動方法とかはgithubのREADMEを参照。

https://github.com/LearnBoost/socket.io

Middleware

1.0では、authorizationが無くなって、Express/Connectライクなmiddlewareの仕組みが導入されました。authorizationは、基本的にsocketの接続を許可するか判定するだけの仕組みでしたが、middlewareでは、handshakeとsocket接続との間に事前処理をいくつでも直列に挿入することができるようになります。接続を切る時は、エラーオブジェクトをnextコールバックに渡します。

// 0.9
io.set('authorizarion', function(handshakeData, callback) {  });
io.of('/foo').authorizarion(function(handshakeData, callback) {  });

// 1.0
io.sockets.use(function(socket, next) {  });
io.of('/foo').use(function(socket, next) {  });

ハンドシェイク

ハンドシェイク時のリクエストデータであるsocket.handshakeは単なるオブジェクトでしたが、1.0では、socket.requestでRequestオブジェクトにアクセスすることができます。この変更のおかげで、Connect用のMiddlewareを使い回すのが簡単になりました。

// Connectのmiddlewareを使って、Expressとcookieを共有する
var http = require('http');
var express = require('express');

function cookieParser(secret) {
  var _cookieParser = express.cookieParser(secret);

  return function(socket, next) {
    var req = socket.request;
    var res = new http.ServerResponse(req); // ダミーのresponseを作る
    _cookieParser(req, res, next);
  }
});

io.sockets.use(cookieParser);
io.on('connection', function(socket) {
  console.log(socket.request.cookie);
});

設定とAdapter

configureメソッドやsetメソッドによる設定機能はなくなって、サーバーのインスタンス作成時にオプションで設定を渡すようになります。別プロセス間で状態を共有する仕組みだったstoreも、Adapterに変更になりましたが、具体的にどう違うかまでは把握できてません :(

// 0.9
io.configure(function() {
  io.set('store', new MemoryStore());
  
});

// 1.0
var io = require('socket.io')(server, {adapter: Adapter});

プロトコル

1.0ではアーキテクチャが刷新され、通信方法の違いによる差異を吸収する汎用のライブラリであるengine.ioの上に、名前空間や自動再接続などの高レベルな機能を提供するsocket.ioが乗るというような構成になっています。
メッセージの形式についても、engine.ioのプロトコルの上にsocket.ioのプロトコルが乗る形になるため、0.9のプロトコルとは全く別で後方互換性はありません。

// 0.9
// 5がpacketのタイプ、それ以降":"区切りでデータ、idなど
5:::{"name":"foo","args":[1]}

// 1.0
// engine.ioのプロトコル: 4がpacketのタイプ、それ以降はデータ
// socket.ioのプロトコル: 2がpacketのタイプ、それ以降はデータ、idなど
42["foo",1]

1.0のJSクライアントを使う場合、プロトコルが変わったことを意識する必要はほとんどないと思いますが、0.9でJS以外のクライアントライブラリを使用している場合、1.0のサーバーとそのまま組み合わせて使うことはできないということになります。

クライアント

クライアントはサーバー側ほど大きく変わっていないようです。

メッセージバッファの廃止

0.9では接続を確立する前にemitでメッセージを送信しようとしても、内部的にメッセージがバッファされ適切なタイミングで送信が行われましたが、1.0ではconnectイベント後に送信しないと、メッセージが無視されてしまいます。

// 0.9
var socket = io.connect('http://localhost');
socket.emit('foo', 1);

// 1.0
var socket = io('http://localhost');
socket.on('connect', function() {
  // connect後にemitする
  socket.emit('foo', 1);
});

これは地味に不便なのでなぜ廃止されたのかわかりませんが、単にまだ実装されていないだけかも :(

その他の変更点

  • イベントが発生する対象が変更。

0.9ではすべてのイベントがSocketで発生しますが、1.0では全体を対象にしたイベントはManagerで発生し、namespaceを対象にしたイベントはそれぞれのSocketで発生するよう変更になったようです。

  • 空白区切りの文字列だったオプションのキーがcamelCaseに変更。
// 0.9
{'force new connection': true}

// 1.0
{forceNew: true}

まとめ

今すぐ製品の一部として使うのは早いと思いますが、これから開発するもので正式リリースを待てるのであれば、現状でも採用を検討してよいレベルにある思います。0.9と1.0の変更の大きさを考えると、真っ先に1.0に対応しておくほうがトータルで楽かも。1.0のリリースは延び延びになってますが、機能自体は期待していいと思います。