Socket.IO能夠實現實時雙向的基於事件的通訊。
它適用於各類平臺,瀏覽器或設備,也一樣注重可靠性和速度。css
socket.io的API比較簡單,能夠很輕鬆的上手,完成一個實時分析圖表或者聊天室之類的程序。html
socket.io在瀏覽器中主要是經過WebSocket來實現實時通訊的,WebSocket是HTML5開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協議。WebSocket通信協議於2011年被IETF定爲標準RFC 6455,WebSocketAPI被W3C定爲標準。Socket.IO除了支持WebSocket通信協議外,還支持許多種輪詢(Polling)機制以及其它實時通訊方式,並封裝成了通用的接口,而且在服務端實現了這些實時機制的相應代碼。Socket.IO實現的Polling通訊機制包括Adobe Flash Socket、AJAX長輪詢、AJAX multipart streaming、持久Iframe、JSONP輪詢等。Socket.IO可以根據瀏覽器對通信機制的支持狀況自動地選擇最佳的方式來實現網絡實時應用。java
在WebSocket API中,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。node
這裏,咱們經過一個web抽獎的小程序,來講明下socket.io的使用。jquery
有時間,咱們想整一個抽獎活動,可是又不肯定參加的人數,或者部分參與人員不在現場。這時,可能咱們須要一個抽獎小程序,控制檯上顯示出一個二維碼,參與人員用手機掃描二維碼或者瀏覽器直接訪問抽籤地址可參與抽獎。有人蔘與抽獎後,控制檯和各個參與者實時顯示當前全部參與者。控制檯點擊抽獎按鈕後,抽出中獎者,各個參與者客戶端實時顯示是否中獎。web
需求很簡單,在沒有socket.io的狀況下,經過setinterval也能夠實現需求,可是在有了socket.io(服務端使用Node.js)的狀況下,實現起來會更加簡單明瞭。redis
{ "name": "socketio-lottery", "version": "0.1.1", "description": "socketio-lottery", "main": "index.js", "author": "lazio10000", "private": true, "license": "BSD", "dependencies": { "express": "4.10.2", "socket.io":"1.4.5", "mongodb":"~2.0" } }
安裝express,方面快速建站。安裝mongodb,用於記錄每次中獎人。mongodb
安裝依賴express
npm install
npm
新增index.js
var express = require('express'); var app = express(); var http = require('http').Server(app); var io = require('socket.io')(http); var port = process.env.PORT || 3000; app.use(express.static(__dirname + '/public')); http.listen(port, function(){ console.log('listening on *:3000'); }); io.on('connection', function(socket){ console.log('a user connected'); });
這裏socketio也監聽3000端口,當有客戶端鏈接時,服務器端會打印日誌:a user connected
增長客戶端:public文件夾下添加client.html
<script src="/socket.io/socket.io.js"></script> <script> var socket = io(); </script>
這裏引用了js版本的socket.io客戶端。運行node index後,瀏覽器訪問,會在服務端打印日誌。
// Setup basic express server var express = require('express'); var app = express(); var server = require('http').createServer(app); var io = require('socket.io')(server); var port = process.env.PORT || 3000; // Setup mongodb client var mongoClient = require('mongodb').MongoClient; var mongoHost = process.env.MONGODB_PORT_27017_TCP_ADDR || 'localhost'; var mongoPort = process.env.MONGODB_PORT_27017_TCP_PORT || 27017; var mongoDatabase = process.env.MONGODB_INSTANCE_NAME || 'test'; var mongoUsername = process.env.MONGODB_USERNAME; var mongoPassword = process.env.MONGODB_PASSWORD; var mongoUrl = "mongodb://" + mongoUsername + ":" + mongoPassword + "@" + mongoHost + ":" + mongoPort.toString() + "/" + mongoDatabase; console.log(mongoUrl); server.listen(port, function () { console.log('Server listening at port %d', port); }); // Routing app.use(express.static(__dirname + '/public')); //參與抽獎人 var usernames = {}; var numUsers = 0; //保存抽獎數據 var insertData = function (winnerList) { var lotteryDate = new Date(); var data = []; winnerList.forEach(function (winner) { data.push({ "LotteryPeriod": lotteryDate, "Winner": winner }); }); //插入數據 mongoClient.connect(mongoUrl, function (err, db) { var collection = db.collection('WinnerList'); collection.insert(data, function (err, result) { if (err) { console.log('Error:' + err); return; } db.close(); }); }); } io.on('connection', function (socket) { //每一個客戶端鏈接都是不一樣的socket實例 var addedUser = false; //控制檯重置事件,用於手動刷新獎池 socket.on('reset', function () { usernames = {}; numUsers = 0; }); //從新獲取獎池 socket.on('reload', function (fn) { fn(usernames); }); //抽獎 socket.on('lottery', function (data) { //保存中獎數據 insertData(data); //清空獎池 usernames = {}; numUsers = 0; //廣播中獎人 socket.broadcast.emit('lottery', { winnerList: data }); }); //登陸 socket.on('add user', function (username) { socket.username = username; if (!usernames[username]) { usernames[username] = username; ++numUsers; addedUser = true; socket.emit('login', { numUsers: numUsers }); socket.broadcast.emit('user joined', { username: socket.username, numUsers: numUsers }); } else { socket.emit('loginError', {}); } }); //客戶端斷開 socket.on('disconnect', function () { if (addedUser) { delete usernames[socket.username]; --numUsers; socket.broadcast.emit('user left', { username: socket.username, numUsers: numUsers }); } }); });
增長控制檯:public文件夾下添加index.html
<!doctype html> <!-- html、websocket抽獎小程序 --> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>抽-抽-抽</title> <link href="bootstrap.min.css" rel="stylesheet"> <link href="bootstrap-theme.min.css" rel="stylesheet"> </head> <body> <div class="container"> <div class="jumbotron" style="margin-top:30px"> <div class="col-md-8"> <h1>抽-抽-抽</h1> <p>請掃描二維碼參加抽獎</p> <p> <button type="button" id="btnLottery" class="btn btn-primary btn-lg btnLottery" data-loading-text="抽ing..." autocomplete="off" data-length="1">找抽</button> <button type="button" id="btnLottery2" class="btn btn-primary btn-lg btnLottery" data-loading-text="抽ing..." autocomplete="off" data-length="2">抽兩個</button> <button type="button" id="btnReset" class="btn btn-default btn-lg" autocomplete="off">重置</span></button> </p> </div> <div class="col-md-4"><img src="2139274403.png" /></div> <p>須要訪問互聯網哦</p> </div> <div> <h1 id="members" style="line-height:80px;"></h1> </div> </div> <script src="jquery-1.10.2.min.js"></script> <script src="bootstrap.min.js"></script> <script src="socket.io-1.0.6.js"></script> <script src="main.js"></script> </body> </html>
對應的main.js
$(function () { var $members = $('#members'); var users = new Array(); var socket = io(); var lotteryState = false; $('#btnReset').click(function(){ socket.emit('reload',function(data){ console.log(data); users = new Array(); $members.empty(); for(var item in data){ users.push(data[item]); $members.append('<span class="label label-default" data-index="' + (users.length - 1) + '">' + item + '</span> '); } }); }); $('.btnLottery').click(function () { if (lotteryState) { return false; } if (users.length === 0 || users.length <= 2) { alert("沒人抽毛"); } else { var lotteryCount = parseInt($(this).attr("data-length")); if (lotteryCount) { $(this).button('loading'); setTimeout(function () { lotteryState = true; var lotteryData = new Array(); function lotteryUser(name, idx, value) { this.name = name; this.idx = idx; this.random = value; } $.each(users, function (i, user) { lotteryData.push(new lotteryUser(user, i, parseInt(10000 * Math.random()))); }); lotteryData.sort(function (a, b) { return a.random < b.random ? 1:-1 }); var lotteryUsername = []; for (var i = 0; i < lotteryCount; i++) { $('span[data-index="' + lotteryData[i].idx + '"]').removeClass('label-default').addClass('label-danger'); lotteryUsername.push(lotteryData[i].name); } socket.emit('lottery', lotteryUsername); lotteryState = false; $('#btnLottery').button('reset'); }, 3000); } } return false; }); socket.on('user joined', function (data) { if (!lotteryState) { users.push(data.username); $members.append('<span class="label label-default" data-index="' + (users.length - 1) + '">' + data.username + '</span> '); } }); socket.on('user left', function (data) { if (!lotteryState) { var userid = $.inArray(data.username, users); if (userid > -1) { $('span[data-index="' + userid + '"]').remove(); users.splice(userid, 1); } } }); });
修改客戶端client.html
<!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>抽-抽-抽</title> <link href="bootstrap.min.css" rel="stylesheet"> <link href="bootstrap-theme.min.css" rel="stylesheet"> <style type="text/css"> body { padding-top: 50px; background-color: #eee; } .starter-template { padding: 40px 15px; text-align: center; } </style> </head> <body> <div class="container"> <div class="starter-template"> <h1>抽-抽-抽</h1> <div id="logon"> <form class="form-inline" role="form" onsubmit="return false"> <div class="form-group"> <input type="text" class="form-control" id="logonName" placeholder="怎麼稱呼,兄臺" maxlength="10"> </div> <button type="button" id="btnSubmit" class="btn btn-primary">肯定</button> </form> </div> <div id="lottery"> <h1 id="stateMessage"></h1> <p id="messages"></p> </div> </div> </div> <!-- /container --> <script src="jquery-1.10.2.min.js"></script> <script src="jquery.cookie.js"></script> <script src="socket.io-1.0.6.js"></script> <script src="client.js"></script> </body> </html>
//客戶端 $(function () { var $loginPage = $("#logon"); var $lotteryPage = $("#lottery"); var username; var connected = false; var socket = io(); var getQueryStringValue = function (keyName) { var searchStr = location.search.substr(1); if (searchStr.length == 0) return null; var collection = searchStr.split('&'); for (var i = 0; i < collection.length; i++) { var tmp = collection[i].split('='); if (tmp.length < 2) continue; if (tmp[0].toUpperCase() == keyName.toUpperCase()) return tmp[1]; } return null; } var login = function () { username = $("#logonName").val().trim(); if (username) { socket.emit("add user", username); } else { $("#logonName").attr("placeholder", "兄臺,怎麼稱呼?"); } }; //自動登陸 var autoLogin = function () { if ($.cookie("UserName") != null) { username = $.cookie("UserName"); socket.emit("add user", username); } } if (getQueryStringValue('autologin') == null) { autoLogin(); } //登陸 $("#btnSubmit").click(function () { login(); }); //手機回車 $(window).keydown(function (event) { if (event.which === 13) { login(); return false; } }); //登陸事件 socket.on("login", function (data) { connected = true; $.cookie("UserName", username, { expires: 365 }); $loginPage.hide(); $lotteryPage.show(); $("#stateMessage").html("等待開獎"); }); socket.on("loginError", function (data) { connected = false; $("#stateMessage").html("換個稱呼吧,朋友"); }); socket.on("user joined", function (data) { $('#messages').html("共有" + data.numUsers + "參與抽籤"); }); socket.on("user left", function (data) { console.log(data); $("#messages").html("共有" + data.numUsers + "參與抽籤"); }); socket.on("lottery", function (data) { if ($.inArray(username, data.winnerList) > -1) { $("#stateMessage").html("恭喜,您中獎了"); $(".container").css("background", "url(55771332451950460.jpg) no-repeat center").css("color", "white"); } else { $("#stateMessage").html("原來沒中獎我也能夠這麼開心"); } }); window.setInterval(function(){ if(!socket.connected){ $("#stateMessage").html("斷線了,刷新吧"); } }, 10000); });
好了,容許服務端,而後分別訪問控制檯和客戶端,咱們的抽獎小程序完成了。
Socket.IO已經具備衆多強大功能的模塊和擴展API:
此外,社區開發者還爲Socket.IO開發了一些開源插件/功能庫: