socket.io簡單說明及在線抽獎demo

socket.io簡單說明及在線抽獎demo

socket.io 簡介

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

socket.on API 說明

  1. io.sockets.on('connection', function (socket) {}); //監聽事件
  2. socket.emit('message', "data"); //發送消息至當前鏈接的客戶端
  3. socket.broadcast.emit('message', "data"); //廣播消息至全部鏈接的客戶端
  4. io.sockets.in('room').emit('message', 'data'); //發送消息至某個頻道的全部客戶端
  5. 等等

開始開發

  • 增長package,json
  • {
      "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 installnpm

  • 新增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後,瀏覽器訪問,會在服務端打印日誌。

  • 服務端增長抽獎等事件:修改index.js
  • // 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>
  • 新增client.js
  • //客戶端 
       $(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:

  • (session.socket.io)(http session中間件,進行session相關操做)
  • socket.io-cookie(cookie解析中間件)
  • session-web-sockets(以安全的方式傳遞Session)
  • socket-logger(JSON格式的記錄日誌工具)
  • websocket.MQ(可靠的消息隊列)
  • socket.io-mongo(使用MongoDB的適配器)
  • socket.io-redis(Redis的適配器)
  • 等等

此外,社區開發者還爲Socket.IO開發了一些開源插件/功能庫:

  • Java客戶端Socket.IO-client.java,能夠用於Android的相關應用中
  • 用於Socket.IO與iOS應用間進行通訊的簡單接口SIOSocket
  • 基於Netty的Socket.IO服務器端的Java實現Netty-socketio
  • 等等
相關文章
相關標籤/搜索