socket.io使用入門

socket.io使用入門

2011-12-29

場景

現在做的項目要提供一個在線編輯器的調試功能,需要實時的把調試的日誌信息傳遞到頁面,可能會有幾個人同時打開編輯器對同一個項目進行操作,這時發送給這些人的調試信息是相同的。
通常這種場景可以通過Comet服務器推,web socket來處理,不過在nodejs中,可以通過socket.io模塊方便的處理時事的通信。

socket.io

socket.io是一個以實現跨瀏覽器、跨平臺的實時應用爲目的的項目。針對不同的瀏覽器版本或者不同客戶端會做自動降級處理,選擇合適的實現方式(websocket, long pull..),隱藏實現只暴露統一的接口。可以讓應用只關注於業務層面上。
nodejs服務器端安裝:npm install socket.io
安裝之後就可以require模塊來使用了:

  var sio = require('socket.io');

在客戶端的話,可以通過

<script src="/socket.io/socket.io.js"></script>

或者引用socket.io的CDN服務。

<script src="http://cdn.socket.io/stable/socket.io.js"></script>

在它的官方網站上有各種用法的介紹。

實際使用

這個項目是搭建在connect之上的,因此需要在connect上使用socket.io,同時,因爲編輯文件有權限限制,因此還需要在socket.io中使用session和一些其他的連接信息來確認權限。
先看一下socket.io的使用, 服務端:

var io = require('socket.io').listen(80);

io.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
    console.log(data);
  });
});

瀏覽器端:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io.connect('http://localhost');
  socket.on('news', function (data) {
    console.log(data);
    socket.emit('my other event', { my: 'data' });
  });
</script>

在服務端收到connection的事件的時候,socket會攜帶一個建立連接時瀏覽器端傳過來的握手信息socket.handshake,我們把它打印出來大概會是下面這個樣子:

  
{ headers: 
   { 
     host: 'cnodejs.net:8080',
     connection: 'keep-alive',
     referer: 'http://cnodejs.net:8080/editor/pipe',
     'user-agent': 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.124 Safari/534.30',
     accept: '*/*',
     'accept-encoding': 'gzip,deflate,sdch',
     'accept-language': 'zh-CN,zh;q=0.8',
     'accept-charset': 'UTF-8,*;q=0.5',
     cookie: 'NAEIDE_console_hide=0; lzstat_uv=7551240663017376909|2341473@2717849; lzstat_ss=2468024318_3_1325124834_2717849; connect.sid=z5sT8ER8SIzyknF6HYnIEdWz.l6oFdxYR24fSV85JIpLcpBabQtqDPB%2BUPm1DR1wqAEU; NAE_c_location=BOTTOM; NAE_c_display=1' 
     },
  address: { address: '123.157.218.120', port: 60285 },
  time: 'Thu Dec 29 2011 02:21:23 GMT+0800 (CST)',
  query: { t: '1325096038995' },
  url: '/socket.io/1/?t=1325096038995',
  xdomain: false,
  secure: undefined
}

這些瀏覽器端的信息得到之後,就很容易進行權限的驗證了。socket.io同時提供了

  
io.set(authorization, callback);

方法來對每個連接進行權限限制。

session與權限驗證

權限驗證非常重要的一部分就是session驗證了,在handshake信息中,可以獲取到瀏覽器端的cookie信息,根據connect(express也一樣)的session機制,在cookie中有一項爲connect.sid,存放了session在服務器端存儲容器中存放的key,通過這個key我們就可以獲取到session值。

  
var io = require('socket.io').listen(app);
var ep = require('EventProxy.js').EventProxy;
var parseCookie = require('connect').utils.parseCookie;
io.set('authorization', function(data, accept){
    var proxy = new ep();
    //get sessionId from cookie & get session from sessionStore
    var parse = function(){
      if(data.headers.cookie){
        //use parseCookie in connect.utils
        data.cookie = parseCookie(data.headers.cookie);
        data.sessionId = data.cookie['connect.sid'];
        //getSession( by connect sessionStore.get)
        SessionStore.get(data.sessionId, function(err, session){
          if(err || !session){
            proxy.unbind();
            return accept(err.toString(), false);
          }else{
            data.session = session;
            proxy.fire('session_got');
          }
        })
      }else{
        proxy.unbind();
        return accept('No cookie transmitted.', false);
      }
    }
    //get auth form database
    var checkAuth = function(){
      //get info in referer
      data.app = getApp(data.headers.referer||'');
      //check auth
      check(data.session.user, data.app, function(err, result){
        if(result){
          accept(null, true);
        }else{
          accept(err?err.message:'permision denied.', false);
        }
      })
    }
    proxy.once('session_got', checkAuth);
    parse();    
  })

通過socket.io完成時事通信

此時所有經過驗證的連接的handshake信息裏,已經多出了app和session的信息,我們把這些連接按照app來分類,因爲所有的app相同的連接,收到的信息也將是相同的。

  
 io.sockets.on('connection', function(socket){  // some socket connect
    var hs = socket.handshake;
    //when socket connect, put this socket into room [hs.app]
    socket.join(hs.app);
    
    //some socket disconnect
    socket.on('disconnect', function(){
      
    });
  });
var proxy = new ep();
//when get stdout data, send msg to sockets in this room
proxy.on('stdout', function(data){
  io.sockets.in(data.room).send(data.log);
})
getData(data){
  proxy.fire('stdout', data);
}

此時只要獲取到了輸出信息,就會通過socket.io傳遞到頁面,觸發頁面的'message'事件,渲染頁面。

關閉debug信息

在socket.io啓用的時候,會不停的打出debug和心跳等信息,在生產環境下我們不想要這麼詳細的輸出,可以通過

  io.set('log level', 1); 

來關閉調試信息的輸出。

總結

socket.io是nodejs實現實時web系統的不二選擇,特別是非常符合nodejs的事件驅動特性,不需要繞彎就能夠完成實時系統。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章