一、作者自述
從事軟件開發3年了,出於各種原因(其實是因為我懶,啊哈哈~),第一次決定動手寫點什么。第一呢,給自己一個總結的機會,梳理下自己的知識庫。第二呢,同第一點。。。
自從大學畢業后,一直處於一種“極度”的繁忙之中。從剛開始入行時各種學習的“飢渴”狀態,到后來工作需要的“填鴨”狀態,一直在學,一直在用,邊用邊學。剛開始接觸的知識面比較窄,但隨着工作的變換,能力的提升,接觸的東西越來越多了。慢慢的,自己越來越感覺知識雜亂,缺乏梳理。因此,決定開通博客,時不時的記錄下自己新學的知識,整理下已經學過的知識,偶爾來靈感了,也可以總結一下。
二、推送服務之socket.io
目前來說,接觸到的推送服務也有幾個了。比如signalr,socket.io,super websocket。除此之外還有其他第三方推送服務。對於一個前后端都得啃的苦逼猿來說,總體感覺原理都是差不多的。最大的不同可能就是其實現方式的不同了。在此不多做追述,有興趣的童鞋可以自行百度。
今天來重點說下socket.io。接觸socket.io是因為公司要開發一款棋牌游戲,決定使用socket.io作為推送服務。和前面說的另外兩個推送不一樣,socket.io是跑在node環境下的服務。不得不說,JS編程確實快捷簡單。沒用過node的童鞋,可以找度娘。廢話不多說,直接上代碼。
socket.io服務端代碼:
1 var app = require('http').createServer(handler) 2 var io = require('socket.io')(app); 3 var fs = require('fs'); 4 5 app.listen(80); 6 7 io.on('connection', function (socket) { 8 socket.emit('news', { hello: 'world' }); 9 socket.on('my other event', function (data) { 10 console.log(data); 11 }); 12 });
socket.io客戶端代碼:
1 <script src="/socket.io/socket.io.js"></script> 2 <script> 3 var socket = io('http://localhost'); 4 socket.on('news', function (data) { 5 console.log(data); 6 socket.emit('my other event', { my: 'data' }); 7 }); 8 </script>
簡單解釋下
<script src="/socket.io/socket.io.js"></script>
該行代碼用來指示socket.io服務所在地址。若服務端與客戶端不在同一項目內,則需要添加上服務所在具體域名或者IP和端口。
至此,我們的推送服務帶客戶端簡單交互已經完成了。當然,相當的簡陋。此例就是官方給出的demo,非常的簡單,直白。但是我們在實際的運用當中,需求肯定不會如此的簡單。
先說下socket.io的部分API,1.0版本之前的API和1.0之后版本的API已經有所不同了(其實我感覺這里描述為事件更貼切)。這里簡單列下1.0版本之后的API。
socket.io 提供了默認事件(如:connect, message, disconnect)。另外,socket.io允許發送並接收自定義事件。
自定義事件:
1 socket.on('send message', function(data) { 2 console.log('send message:::::::::', data); 3 });
事件的定義基本不變。所有的事件基本都是這個格式。
執行事件:
socket.emit("send message", {msg: 'test send messsage'});
這是個最基本的事件執行代碼。其作用是返回一段提示信息給自己。其他人無法接收到。如果我們要給別人發送消息怎么辦呢??
io.sockets.connected[socket.id].emit('send message', {msg: 'test specific message'});
此行代碼的作用就是給特定socket發送消息。只需要知道目標的socketId即可。因此,服務端最好要實現處理用戶和socke關系的業務。
socket.broadcast.to(socket.id).emit('send message', {msg: 'test specific message by room'});
這行代碼的效果和上面一樣是一樣的。用戶在建立socket鏈接后,默認都會加入一個room,該room的ID就是socket的ID。相當於一個特定的房間內只有一個用戶。我們想該房間通知消息,該用戶就可以接收到。不同的是上面的代碼可以給自己發送消息,而本行代碼不行,只能給別人發送消息。
廣播消息:
1 //全局廣播消息(不包含自己) 2 socket.broadcast.emit('broadcast message', {msg: 'test broadcast message'}); 3 //房間內廣播消息(不包括自己) 4 socket.broadcast.to('cg room').emit('broadcast message to room', {msg: 'test broadcast message to room'}); 5 //全局廣播消息(包括自己) 6 io.sockets.emit('broadcast message all', {msg: 'test broadcast message to all'}); 7 //房間內廣播消息(包括自己) 8 io.sockets.to('cg room').emit('broadcast message all in room', {msg: 'test broadcast message to all in room'});
這幾行代碼的作用是向某一范圍內,所有已鏈接的socket發送消息。網上看到想房間內發送消息的時候,有的用in,有的用to,區別是in自己可以接收到,to自己接收不到。但我測試的結果是這倆其實是一樣的。想房間內通知消息,是否需要屏蔽自己,也不是通過in和to來區分的。這可能是老版本和新版本的區別吧。
推送,其實就是客戶端A向服務器主動發送一條消息,服務器收到並根據要求進行轉發,此時B客戶端作為接收方,被動的接收服務器轉發過來的數據。當然,中間涉及到用戶狀態的變更和維護。這里不做描述。而某些情況下,我們需要知道服務器或者客戶端是否收到了發送過去的數據,我們可以發送帶有確認機制的消息:
//Sending and getting data (acknowledgements) socket.emit('ack message', {msg: 'test ack message'}, function (data){ //回掉返回的確認消息,可以為任意JS支持的類型 console.log(data); });
其對應的事件定義方式需要改動一下:
socket.on('ack message', function(data, fn) { console.log('ack message:::::::::', data); fn(true); });
此外,socket.io還提供一種消息發送機制volatile messages。意思大概是說,當服務器發送數據時,客戶端因為各種原因不能正常接收,比如網絡問題、或者正處於長連接的建立連接階段。此時會讓我們的應用變得 suffer(意會一下),那就需要考慮發送 volatile 數據。即使客戶端沒連線,一樣可以這樣發送,服務器會自動丟棄發送失敗的數據。這里提供一個官方demo:
1 var io = require('socket.io')(80); 2 3 io.on('connection', function (socket) { 4 var tweets = setInterval(function () { 5 getBieberTweet(function (tweet) { 6 socket.volatile.emit('bieber tweet', tweet); 7 }); 8 }, 100); 9 10 socket.on('disconnect', function () { 11 clearInterval(tweets); 12 }); 13 });
房間和命名空間
這里不再具體贅述,官網給的demo已經和詳細了。有需求的同學可以自行查看文檔。
最后
第一次寫博客,很多地方都寫的比較簡陋,望請諒解!