Socket.io 的說話島

socket io 是 nodejs 所提供的套件,它主要可以做的事情就是推播功能

你想想,假設你要做個股票報價網站,然後當你後端收到新的股價時,你要如何的送到前端 ? 在傳統的 server 與 client 架構下,因為只能由 client 向 server 發出請求,而不能由 server 發送新的訊息到 client,所以當時的人們的解決方案就是輪詢,固名思意就是指定時的去 server 找資料。

但這種方案有缺點,你想想,你有可能去 server 抓 10 次資料,它有可能 10 次都有新的資料嗎 ? 不一定對吧 ? 所以最理想的方案一定是從 server 端有新資料就自動推送到 client 端。

websocket就是一個由 html 5 所發布的新協議,它就可以做到上面所需要的功能。

socket.io是啥 ? 它是會根據你的 client 所支援的功能(websocket、comet、長輪詢…)來決定你後端要如何的發送資料,更白話文的說,你不用管你的 client 有沒有支援 websocket,socket.io 一切都自動會處理好,你只要和我說啥時要送資訊到前端就對了。

Socket.io 的組成

請參考筆者的這篇文章。

Socketio 的架構

簡單 client 與 server 的溝通範例

server 端程式碼如下,這段程式碼當與 client 端建立一條 websocket 連線後,會直接對該條連線傳送個{hello: "world"}訊息。

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

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:8080');
    socket.on('news', function (data) {
        console.log(data);
        socket.emit('my other event', { my: 'data' });
    });
</script>

這樣就可以完成簡單的推播功能囉。

Socket.io 的 Rooms 與 Namespaces

在 socket.io 有兩個很重要的概念roomsnamespaces,這邊你只需要記好一件事,那就是它們兩個存在的原因都是為了分組,把要傳送的訊息,送到你想要的群組中。

Namespaces

我們先看看namespaces,上面的範例中有沒有注意到,我們都是用io來進行所有的操作,假設我們要放送訊息到所有連線的 socket,那我們只要下達該指令 :

io.emit('news', 'Hi I am Mark')

接下來然後我們也可以使用 rooms 來將 io 裡面的 socket 分類到不同的房間中,所以當我們要傳送指定的訊息到某個房間中只要下達下面的指令 :

io.to('全家就是我家').emit('news', 'Hi I am Mark')

那這邊我想問問,有沒有辦法建立另一個 io 呢 ? 例如我想建立一個是專門處理股票的 io ,而另一個是專門處理期貨的 io ,這時我們就可以使用namespaces裡的of這方法來處理。

例如我們先來建立一個股價的 namespaces :

var stock_io = io.of('/stock');

然後我們就可以使用這個stock_io來進行我們上面提到的所有動作。

namespaces 你可以想成, 子 io ,它可以做所有 io 可以做的事情 (基本上)

Room

接下來我們來說說rooms,這東東的概念和namespace事實上很像,但記好,rooms 是在 namespaces 底下的

假設你有一個需求,有3個用戶在用你的股價報價系統,其中兩個是在看 1101 台泥的股價,而其中一個是在看 2330 台積電 的股價,這時你應該要注意,不能將 2330 的股價推到在看 1101 股價的使用者那。

socket.io 的 rooms 的功能就可以解決上面的需求,rooms的功能白話文就是你可以發送訊息到指定的房間,像 1101 就是一個房間,而 2330 就是另一個房間,所以假設有 1101 的訊息要推送,只要針對該 room 的用戶進行推送就夠了。

那要如何加入到房間呢 ?

如下 :

io.on('connection', function(socket){
  socket.join('1101');
});

那要如何傳送訊息到指定的房間呢 ?

如下園式碼,你就可以發送訊息到這個房間裡。注意這邊是用io來發送訊息,上面簡單的範例是用socket.emit來送是因為上面範例只需要發送訊息給那條 socket 連線就好,而這邊我們是要發送給1101 這個 room中的 socket,所以要用io來發送。

 io.to('1101').emit('xxxx股價')

這兩個的差別

最後終結一下這兩個的差別

你可以把 namespace 的功能想成可以建立多個子 io ,不同的子 io 可以處理自已的事情,也代表有自已的 rooms ,io1 與 io2 兩個如果都有 room 為 movie 的,也不會影響到什麼,因為它們是分屬不同的 io 了。

在 Socket.io 中使用 middleware

在平常我們 web 開發時,有時後會有下面這種需求 :

每一個 http 請求進來前,需要先檢查登入狀態

通常這種時後我們就會建立一個 middleware 來處理登入狀態,這邊要注意 middleware 不是用來專門處理登入的東東,它只是一個概念,它真正的定義如下 :

middleware 又稱中介層,用來處理所有進入或離開主體前的事務。

像我們每個 http 請求進到主體前,我們需要先確認它的狀態,又或者是進到主題前就先將 log 寫好,這些都是可以放置到middleware來處理,你只要記好,主體就只做主體要做的事情就好。

當然在 socket.io 中我們也是有這個需求,例如每當要建立 websocket 連線時,要預先處理的事情,我們都可以建立 middleware 來處理。

socket.io 提供use方法來讓我們建立 middleware ,範例程式碼如下 :

var srv = require('http').createServer();
var io = require('socket.io')(srv);
var run = 0;
io.use(function(socket, next){
  run++; // 0 -> 1
  next();
});

var socket = require('socket.io-client')();
socket.on('connect', function(){
  // run == 2 at this time
});

像上面的官方程式碼中,下面這段就是 midddleware 的使用方法,裡面的 use 就是一個 middleware 方法,在建立 websocket 前會先處理的事情。

io.use(function(socket, next){
  run++; // 0 -> 1
  next();
});

所以假設我們需要先處理log 事務快取事務時,我們程式碼就大概會長如下 :

io.use(logMiddleWare);
io.use(cacheMiddleWare);

function logMiddleWare(socket, next){
     log ~~~
    next();
}

function cacheMiddleWare(socket, next){
    取得暫存資料.....
    next();
}

參考資料

comments powered by Disqus