socket.io简单说明及在线抽奖demo
socket.io简单说明及在线抽奖demo
socket.io 简介
Socket.IO可以实现实时双向的基于事件的通信。
它适用于各种平台,浏览器或设备,也同样注重可靠性和速度。
socket.io的API比较简单,可以很轻松的上手,完成一个实时分析图表或者聊天室之类的程序。
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能够根据浏览器对通讯机制的支持情况自动地选择最佳的方式来实现网络实时应用。
在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
这里,我们通过一个web抽奖的小程序,来说明下socket.io的使用。
需求说明
有时间,我们想整一个抽奖活动,但是又不确定参加的人数,或者部分参与人员不在现场。这时,可能我们需要一个抽奖小程序,控制台上显示出一个二维码,参与人员用手机扫描二维码或者浏览器直接访问抽签地址可参与抽奖。有人参与抽奖后,控制台和各个参与者实时显示当前所有参与者。控制台点击抽奖按钮后,抽出中奖者,各个参与者客户端实时显示是否中奖。
需求很简单,在没有socket.io的情况下,通过setinterval也可以实现需求,但是在有了socket.io(服务端使用Node.js)的情况下,实现起来会更加简单明了。
socket.on API 说明
- io.sockets.on('connection', function (socket) {}); //监听事件
- socket.emit('message', "data"); //发送消息至当前连接的客户端
- socket.broadcast.emit('message', "data"); //广播消息至所有连接的客户端
- io.sockets.in('room').emit('message', 'data'); //发送消息至某个频道的所有客户端
- 等等
开始开发
增加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,用于记录每次中奖人。
安装依赖
npm install新增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
- 等等
socket.io简单说明及在线抽奖demo的更多相关文章
- 在线白板,基于socket.io的多人在线协作工具
首发:个人博客,更新&纠错&回复 是昨天这篇博文留的尾巴,socket.io库的使用练习,成品地址在这里. 代码已经上传到github,传送门.可以开俩浏览器看效果. 现实意义是俩人在 ...
- vue.js+socket.io+express+mongodb打造在线聊天
vue.js+socket.io+express+mongodb打造在线聊天 在线地址观看 http://www.chenleiming.com github地址 https://github.com ...
- vue.js+socket.io+express+mongodb打造在线聊天[二]
vue.js+socket.io+express+mongodb打造在线聊天[二] 在线地址观看 http://www.chenleiming.com github地址 https://github. ...
- Node中的Socket.IO 简单Demo及说明
注:下面Demo的Server和Client都是纯后端. 并没有web页面. Server端代码: var express = require('express'); var app = expres ...
- socket.io简单入门(一.实现简单的图表推送)
引子:随着nodejs蓬勃发展,虽然主要业务系统因为架构健壮性不会选择nodejs座位应用服务器.但是大量的内部系统却可以使用nodejs试水,大量的前端开发人员转入全堆开发也是一个因素. 研究本例主 ...
- Node.js、Express、Socket.io 入门
前言 周末断断续续的写了第一个socket.io Demo.初次接触socket.io是从其官网看到的,看着get started做了一遍,根据官网的Demo能提供简单的服务端和客户端通讯. 这个De ...
- 利用socket.io实现多人聊天室(基于Nodejs)
socket.io简单介绍 在Html5中存在着这种一个新特性.引入了websocket,关于websocket的内部实现原理能够看这篇文章.这篇文章讲述了websocket无到有,依据协议,分析数据 ...
- 运用socket实现简单的服务器客户端交互
Socket解释: 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. Socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意 ...
- 后端Python3+Flask结合Socket.io配合前端Vue2.0实现简单全双工在线客服系统
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_158 在之前的一篇文章中:为美多商城(Django2.0.4)添加基于websocket的实时通信,主动推送,聊天室及客服系统,详 ...
随机推荐
- Oracle 常用SQL技巧(转)
1. SELECT子句中避免使用 “*”当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 ‘*’ 是一个方便的方法.不幸的是,这是一个非常低效的方法. 实际上,ORACLE在解 ...
- 曲演杂坛--使用CTE时踩的小坑:No Join Predicate
在一次系统优化中,意外发现一个比较“坑”的SQL,拿出来供大家分享. 生成演示数据: --====================================== --检查测试表是否存在 IF(O ...
- linux下如何打包压缩?解包解压?.tar文件.gz文件
===文件打包.压缩 ==打包 tar [root@521478.com]# tar -cvf etc1.tar /etc //c创建 v详细 f打包后文件名 [root@521478.com]# t ...
- git pull
今天在服务器上git pull是出现以下错误: error: Your local changes to the following files would be overwritten by mer ...
- php 批量更新某字段内容的部分内容 replace(要替换的字段,'被替换的字符串,'替换成的字符串')
要求: 一个字段值: ------预约---- 要将其中 "预约",改成"预定". 但是我开始写的时候,写成了 update 表名 set smscontent ...
- linux tar命令简介
一.使用介绍 1.名词区分 打包:将一大堆文件或目录变成一个总的文件[tar命令] 压缩:将一个大的文件通过一些压缩算法变成一个小文件[gzip,bzip2等] Linux中很多压缩程序只能针对一个文 ...
- [转]ASP.NET MVC IOC 之AutoFac攻略
本文转自:http://www.cnblogs.com/WeiGe/p/3871451.html 一.为什么使用AutoFac? 之前介绍了Unity和Ninject两个IOC容器,但是发现园子里用A ...
- [转]How to open specific page in the application by clicking on the notification
本文转自:https://github.com/phonegap-build/PushPlugin/issues/213 问:Hello, I would like to know how can I ...
- VIJOS P1426兴奋剂检查[DP 状态哈希]
背景 北京奥运会开幕了,这是中国人的骄傲和自豪,中国健儿在运动场上已经创造了一个又一个辉煌,super pig也不例外……………… 描述 虽然兴奋剂是奥运会及其他重要比赛的禁药,是禁止服用的.但是运动 ...
- 精通CSS version2笔记之⒈选择器
1.常用的选择器:①元素选择器 指定希望应用样式的元素.比如:p {color:#fff;}②后代选择器 寻找特定元素或者元素的后代. 比如:body p{color:#ccc;} 这个选 ...