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开发了一些开源插件/功能库: