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的实时通信,主动推送,聊天室及客服系统,详 ...
随机推荐
- 【故障处理】ORA-28040: No matching authentication protocol
[故障处理]ORA-28040: No matching authentication protocol 1.1 BLOG文档结构图 1.2 前言部分 1.2.1 导读和注意事项 各位技术爱好者 ...
- jsp有哪些内置对象?作用分别是什么?分别有什么方法?
JSP共有以下9个内置的对象: request 用户端请求,此请求会包含来自GET/POST请求的参数 response 网页传回用户端的回应 pageContext 网页的属性是在这里管理 sess ...
- java 读写word java 动态写入 模板文件
import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import ja ...
- ubuntu下apache2 安装 配置 卸载 CGI设置 SSL设置
一.安装.卸载apache2 apache2可直接用命令安装 sudo apt-get install apache2 卸载比较麻烦,必须卸干净,否则会影响ap ...
- Linux的文件时间
在windows下,一个文件有:创建时间.修改时间.访问时间.而在Linux下,一个文件也有三种时间,分别是:访问时间.修改时间.状态改动时间. 1.访问时间,读一次这个文件的内容,这个时间就会更新. ...
- php遍历循环数组实现方法
简单利用foreach for list each while来遍历数组,包括普通的一维数组与二维数组遍历方法,下面详细的介绍了每个函数的使用方法. $foreach = array(1,2,3); ...
- linux centos使用xrdp远程界面登陆
redhat6 安装xrdp 直接使用windows远程桌面连接登陆 下面介绍实现方法: 第一步:下载源码包,并安装一些依赖的软件下载xrdp源码包 wget http://downloads.so ...
- linux shell 之 crontab(定时任务)详解
1.定义: crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行.该词来源于希 ...
- hadoop如何处理长时间运行不完成的map/reduce 任务?
如果某一个任务在某个节点上长时间不完成,怎么手动干预来处理这种情况?董西成博客上找到的回答:hadoop中有三种特殊的任务,failed task,killed task和speculative ta ...
- 理解 OpenStack + Ceph (8): 基本的 Ceph 性能测试工具和方法
本系列文章会深入研究 Ceph 以及 Ceph 和 OpenStack 的集成: (1)安装和部署 (2)Ceph RBD 接口和工具 (3)Ceph 物理和逻辑结构 (4)Ceph 的基础数据结构 ...