2013年6月8日土曜日

websocket.ioを使ってブロードキャストするときに注意すること

最近Node.js+websocket.ioな環境を作って遊んでいたのだけど、クライアント切断時にサーバー側でエラーが頻出するのでwebsocket.ioのソースコードを見てみました

サーバー側のソースコードはこんな感じ
受け取ったメッセージをブロードキャストするシンプルなコードです
送信元にも送信しちゃいます



var webSocket = require('websocket.io');
var server = webSocket.listen(8080, function() {
 console.log('connect');
});

server.on('connection', function(socket) {
 socket.on('message', function(data) {
  server.clients.forEach(function(client) {
   client.send(data);
  });
 });
});


実際に動作させてクライアントを切断すると、例外が出たり出なかったり・・・

例外を表示してやると

[TypeError: Cannot call method 'send' of null]

とのことで、明らかにclientにnullが代入されてしまっています

タイミング的な問題だったら面倒だなぁと思いつつ、websocket.ioのソースコードを見てみたら別に何のことはない処理が原因でした

https://github.com/LearnBoost/websocket.io/blob/master/lib/server.js
66行目あたり

  client.on('open', function() {
    if (self.options.clientTracking) {
      var i = null;
      if (self.clientsNull.length) {
        i = self.clientsNull.shift();
        self.clients[i] = client;
      }
      else {
        i = self.clients.length;
        self.clients.push(client);
      }
      self.clientsCount++;
      client.on('close', function () {
        self.clients[i] = null;
        self.clientsNull.push(i);
        self.clientsCount--;
      });
    }

client.on('close'... でclientsにnullが突っ込まれてます
さらに、clientNullでnullのインデックスを記録しといて、配列長を節約している模様

つまり、Server#clientsには現在接続中のクライアントと、null値が混在しているので
きちんとnullチェックをしてあげましょうということ
ついでに言えば、現在接続中のクライアント数を取得するには、Server#clients.lengthではなくServer#clientsCountを参照する必要があります

というわけで、最初のコードは以下のような感じになります


var webSocket = require('websocket.io');
var server = webSocket.listen(8080, function() {
 console.log('connect');
});

server.on('connection', function(socket) {
 socket.on('message', function(data) {
  server.clients.forEach(function(client) {
   // nullははじこう
   if (client != null) {
    client.send(data);
   }
  });
 });
});


ドキュメントに書いてありそうな内容ですが、今のところまだ見つけられず
ソース見てねってことなのかな

0 件のコメント:

コメントを投稿