基于SignalR的消息推送与二维码描登录实现
1 概要说明
使用微信扫描登录相信大家都不会陌生吧,二维码与手机结合产生了不同应用场景,基于二维码的应用更是比较广泛。为了满足ios、android客户端与web短信平台的结合,特开发了基于SinglarR消息推送机制的扫描登录。本系统涉及到以下知识点:
SignalR:http://signalr.net/ 这官网,ASP.NET SignalR 是为 ASP.NET 开发人员提供的一个库,可以简化开发人员将实时 Web 功能添加到应用程序的过程。实时 Web 功能是指这样一种功能:当所连接的客户端变得可用时服务器代码可以立即向其推送内容,而不是让服务器等待客户端请求新的数据。
二维码:使用的QRCode类库,https://github.com/jeromeetienne/jquery-qrcode
MVC5:开发环境是基于MVC5

2、系统关系图
在实现本功能前,有点不是太确定能否拿下。
所谓万事开头难,通过查询想资料及自己归纳分析:系统涉及到手机客户端、浏览者、服务端,实现扫描登录也就是三者之间是如何协调工作的。通过axure画出如下关系图:

移动客户端、浏览者、服务端三者协作关系图
【M】:表示移动端 【B】:表示浏览者(浏览器客户端) 【S】:服务端,消息推送者及扫描认证接口发布者
步骤说明:
Step(步骤)1 ,【B】浏览登录页面,Step2【S】产生一个标识符UUID,并推送给B,生成登录二维码;
Step3,【M】扫描二维码,前提条件是【M】已登录,Step4【M】解析二维码信息获取UUID;
Step5,【M】向服务端发送UUID+登录信息,Step6【S】对UUID+登录信息进行相关解析认证,Step6 UUID认证,不通过认证,则到Step6-1 重新生成UUID循环Step 2与并Step6-2 返回给【M】UUID认证失败原因,Step6 通过认证,Step6-2转到登录信息认证,Step 7登录信息认证,失败Step7-3重新生成UUID循环Step 2,成功则Step7-1推送给【B】跳转到首页。
3、SignalR循环消息推送
3.1 引用SignalR
由于本人用的是VS15Preview4,可以直接使用Nuget可视化管理工具进行安装:Tools—>Nuget Package Manager—>Manage Nuget Packages for Solution…,打开以下界面:

在Browser 标签下输入SignalR,查询到Microsoft.AspNet.SignalR

找到对应的项目,点击“Install”安装按钮即可引用相关类库,同时应用下载相关js库。
关于SignalR的知识点,可以到官网 http://www.asp.net/signalr 进行深入学习。
3.2 服务端SignalR实现
服务端要向客户端推送UUID,对于UUID唯一标识符,具有重要特性:(1)有时间限制,120秒之内扫码有效;(2)具有一定的状态。对应的声明周期就是:生成—>推送—>状态判断—>手机端扫描—>验证UUID—>状态判断—>销毁等系列过程。
服务端的核心代码将单独建立一个项目去实现:

3.2.1 Nofifier.cs通知类
本类将连接QRCodeHub与SessionTimer
using Microsoft.AspNet.SignalR; namespace TxSms.SingalR
{
public static class Notifier
{
private static readonly IHubContext Context = GlobalHost.ConnectionManager.GetHubContext<QRCodeHub>(); public static void SessionTimeOut(string connectionId, int time)
{
Context.Clients.Client(connectionId).alertClient(time);
} public static void SendElapsedTime(string connectionId, int time)
{
Context.Clients.Client(connectionId).sendElapsedTime(time);
} public static void SendQRCodeUUID(string connectionId, string uuid)
{
Context.Clients.Client(connectionId).sendQRCodeUUID(uuid);
}
}
}
3.2.2 QRCodeHub.cs SignalR核心实现
SignalR的核心代码:
using Microsoft.AspNet.SignalR;
using System.Threading.Tasks; namespace TxSms.SingalR
{
/// <summary>
/// 二维码推送
/// </summary>
//[HubName("qrcode")]
public class QRCodeHub : Hub
{
/// <summary>
/// 给客户端发送时间间隔
/// </summary>
/// <param name="time"></param>
public void SendTimeOutNotice(int time)
{
Clients.Client(Context.ConnectionId).alertClient(time);
} public void CheckElapsedTime(int time)
{
Clients.Client(Context.ConnectionId).sendElapsedTime(time);
} /// <summary>
/// 发送二维码UUID内容
/// </summary>
/// <param name="uuid"></param>
public void SendQRCodeUUID(string uuid)
{
Clients.Client(Context.ConnectionId).sendQRCodeUUID(uuid);
} /// <summary>
/// Called when the connection connects to this hub instance.
/// </summary>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /></returns>
public override Task OnConnected()
{
SessionTimer.StartTimer(Context.ConnectionId);
return base.OnConnected();
} /// <summary>
/// Called when a connection disconnects from this hub gracefully or due to a timeout.
/// </summary>
/// <param name="stopCalled">
/// true, if stop was called on the client closing the connection gracefully;
/// false, if the connection has been lost for longer than the
/// <see cref="P:Microsoft.AspNet.SignalR.Configuration.IConfigurationManager.DisconnectTimeout" />.
/// Timeouts can be caused by clients reconnecting to another SignalR server in scaleout.
/// </param>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /></returns>
public override Task OnDisconnected(bool stopCalled)
{
SessionTimer.StopTimer(Context.ConnectionId);
return base.OnDisconnected(stopCalled);
} /// <summary>
/// Called when the connection reconnects to this hub instance.
/// </summary>
/// <returns>A <see cref="T:System.Threading.Tasks.Task" /></returns>
public override Task OnReconnected()
{
if (!SessionTimer.Timers.ContainsKey(Context.ConnectionId))
{
SessionTimer.StartTimer(Context.ConnectionId);
}
return base.OnReconnected();
} /// <summary>
/// 重置时钟
/// </summary>
public void ResetTimer()
{
SessionTimer timer;
if (SessionTimer.Timers.TryGetValue(Context.ConnectionId, out timer))
{
timer.ResetTimer();
}
else
{
SessionTimer.StartTimer(Context.ConnectionId);
}
} /// <summary>
/// 发送普通消息
/// </summary>
/// <param name="name"></param>
/// <param name="message"></param>
public void Send(string name, string message)
{
Clients.All.addNewMessageToPage(name, message);
}
}
}
3.2.3 SessionTimer.cs 对应客户端时钟
对【B】来说,产生一个独立的timer,进行按1s间隔发送消息。
using System;
using System.Collections.Concurrent;
using System.Timers; namespace TxSms.SingalR
{
public class SessionTimer : IDisposable
{
/// <summary>
/// 存储客户端对应的Timer
/// </summary>
public static readonly ConcurrentDictionary<string, SessionTimer> Timers; private readonly Timer _timer; static SessionTimer()
{
Timers = new ConcurrentDictionary<string, SessionTimer>();
} /// <summary>
/// 构造函数
/// </summary>
/// <param name="connectionId"></param>
private SessionTimer(string connectionId)
{
ConnectionId = connectionId;
_timer = new Timer
{
Interval = Utility.ActivityTimerInterval()
};
_timer.Elapsed += (s, e) => MonitorElapsedTime();
_timer.Start();
} public int TimeCount { get; set; } /// <summary>
/// 客户端连接Id
/// </summary>
public string ConnectionId { get; set; } /// <summary>
/// 启动Timer
/// </summary>
/// <param name="connectionId"></param>
public static void StartTimer(string connectionId)
{
var newTimer = new SessionTimer(connectionId);
if (!Timers.TryAdd(connectionId, newTimer))
{
newTimer.Dispose();
}
} /// <summary>
/// 停止Timer
/// </summary>
/// <param name="connectionId"></param>
public static void StopTimer(string connectionId)
{
SessionTimer oldTimer;
if (Timers.TryRemove(connectionId, out oldTimer))
{
oldTimer.Dispose();
}
} /// <summary>
/// 重置Timer
/// </summary>
public void ResetTimer()
{
TimeCount = ;
_timer.Stop();
_timer.Start();
} public void Dispose()
{
// Stop might not be necessary since we call Dispose
_timer.Stop();
_timer.Dispose();
} /// <summary>
/// 给客户端发送消息
/// </summary>
private void MonitorElapsedTime()
{
Utility.ClearExpiredUUID();
var uuid = Utility.GetUUID(ConnectionId);
//if (TimeCount >= Utility.TimerValue())
//{
// StopTimer(ConnectionId);
// Notifier.SendQRCodeUUID(ConnectionId, uuid);
// Notifier.SessionTimeOut(ConnectionId, TimeCount);
//}
//else
//{
Notifier.SendQRCodeUUID(ConnectionId, uuid);
Notifier.SendElapsedTime(ConnectionId, TimeCount);
//}
TimeCount++;
if (TimeCount > )
{
TimeCount = ;
}
}
}
}
3.2.4 Utility.cs 基础配置
满足时钟、获取QRCode等
using TxSms.Actions; namespace TxSms.SingalR
{
internal class Utility
{
public static int IntNum = ; /// <summary>
/// 时间间隔
/// </summary>
/// <returns></returns>
public static int TimerValue()
{
return ;
} public static double ActivityTimerInterval()
{
return 1000.0;
} /// <summary>
/// 获取当前UUID
/// </summary>
/// <returns></returns>
public static string GetUUID(string connectionId)
{
try
{
var model = new QRCodeAction().GetValidModel(connectionId);
return model.ToJson(connectionId);
}
catch
{
return "ERROR";
}
} /// <summary>
/// 删除过期UUID
/// </summary>
public static void ClearExpiredUUID()
{
IntNum++;
if (IntNum <= ) return;
new QRCodeAction().ClearExpiredUUID();
IntNum = ;
}
}
}
3.2.5 SignalR在MVC中启动配置
在MVC中,启动项目进行如下配置:
using Microsoft.Owin;
using Owin; [assembly: OwinStartup(typeof(TxSms.Web.Startup))] namespace TxSms.Web
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
//启动SignalR
app.MapSignalR();
ConfigureAuth(app);
}
}
}
3.2.6 其他类库说明
QRCodeAction.cs:维护UUID,创建、保存、状态更改、删除等。
QRModel.cs:UUID实体
所有文件,可在《7、总结与下载》中下载。
3.3 客户端SignalR实现
添加SignalR js库:
<script type="text/javascript" src="~/Scripts/jquery.signalR-2.2.1.min.js"></script>
<script type="text/javascript" src="~/signalr/hubs"></script
两者必须都引用。
调用接口如下:
var codeUUID = "";
$(function () {
// Reference the auto-generated proxy for the hub.
var qrcode = $.connection.qRCodeHub;
// Create a function that the hub can call back to display messages.
qrcode.client.addNewMessageToPage = function (name, message) {
// Add the message to the page.
console.log(message);
//jQuery('#divQRCode').qrcode({ width: 180, height: 180, correctLevel: 0, text: message });
};
qrcode.client.sendElapsedTime = function (time) {
console.log(time);
};
qrcode.client.sendQRCodeUUID = function (uuid) {
console.log("sendQRCodeUUID");
console.log(codeUUID);
if (codeUUID === uuid) {
return;
}
codeUUID = uuid;
if (codeUUID !== "ERROR") {
var jsonUUID = $.parseJSON(codeUUID);
if (jsonUUID.islogin === 1) { //判断是否登录
window.location.href = "/Home/Index/@Model.Name";
}
}
$("#divQRCode").html("");
$('#divQRCode').qrcode({ width: 180, height: 180, correctLevel: 0, text: codeUUID });
};
// Start the connection.
$.connection.hub.start().done(function () {
//qrcode.server.updateConnectionId($.connection.hub.id);
qrcode.server.send("qrcode", Math.random());
});
});
以上代码包括相关二维码的生成。
4、二维码的生成与存储数据解析
4.1 二维码的生成
二维码类库选择https://github.com/jeromeetienne/jquery-qrcode 一个QRCode原生态js类库,jquery对其进行了扩展。
添加script标签:
<script type="text/javascript" src="~/Scripts/qrcode.min.js"></script>
<script type="text/javascript" src="~/Scripts/jquery.qrcode.min.js"></script>
定义div标签,用来呈现二维码:
<!--二维码登录开始-->
<div class="ewmcode_login" id="ewmcode_login">
<div class="codeText">安全登录 防止被盗</div>
<div id="divQRCode" class="codebox" style="background:none;"></div>
<div class="coderemindText">扫一扫登录</div>
</div>
<!--二维码登录结束-->
呈现二维码:
$("#divQRCode").html("");
$('#divQRCode').qrcode({ width: 180, height: 180, correctLevel: 0, text: codeUUID });
通过3与4,可实现具有180秒生命周期二维码的生成,对于不同的浏览者,生成的二维码是不同的,效果如下:

4.2 二维码存储的是什么
二维码生成了,但是存储的是什么呢?首先我们看下以下的二维:
![]() |
![]() |
|
hbuilder官网 |
千牛电脑客户端二维码登录界面 |
显然,扫描这两个图片上的二维码会得到不同的结果。对某些二维码的解码要对应配套的客户端才能起到作用,否则用其他工具解析出来也就是字符串。
在本系统中,二维码存储的是一个json对象,格式为:
{"connectionid":"19c12e95-26d7-410c-8292-2a3afdd1a4da","uuid":"a04702df-6a52-4e1c-be8b-9b3dbeef4d72","islogin":0,"isvalid":1}
connectionid:客户端与SignalR联系的id,其格式为Guid
- uuid:对应connectionid产生的一个唯一标识符,其格式为Guid
- islogin:当前connectionid连接是否已登录,1—>表示登录,0—>未登录
- isvalid:当前connectionid对应的uuid是否有效,1—>表示有效,0—>表示失效
手机客户端扫描之后,可根据这些参数情况进行判断,是否向服务端发送请求。在做扫描应用(比如扫描登录)时,要依据业务场景进行消息传递,生成对应二维码,并不局限于json对象、url地址等。
总结下来,二维码应用场景,如下图:

5、扫描认证接口
为了满足【M】端扫描之后,提交UUID+用户信息进行认证,建立QRCode API接口。接口任务比较简单,就是对UUID合法性进行判断,然后判断用户信息登录情况,更改UUID的登录状态。
5.1 输入参数
using Abp.Application.Services.Dto;
using System;
using System.ComponentModel.DataAnnotations; namespace TxSms.Inputs
{
/// <summary>
/// 二维码登录认证
/// </summary>
[Serializable]
public class QRCodeVerifyInput : IInputDto
{
/// <summary>
/// 构造函数
/// </summary>
public QRCodeVerifyInput()
{
ConnectionId = Guid.Empty.ToString();
UUID = Guid.Empty;
UserName = Password = "";
} /// <summary>
/// 当前回话ID
/// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string ConnectionId { get; set; } /// <summary>
/// 唯一标识符号
/// </summary>
public Guid UUID { get; set; } /// <summary>
/// 用户账号
/// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string UserName { get; set; } /// <summary>
/// 登录密码
/// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Password { get; set; } /// <summary>
/// 平台
/// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
public string Platform { get; set; }
}
}
5.2 输出参数
using Abp.Application.Services.Dto;
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using TxSms.MVC; namespace TxSms.Outputs
{
/// <summary>
/// 输出基类
/// </summary>
[ModelBinder(typeof(EmptyStringModelBinder))]
public class TxSmsOutputDto : IOutputDto
{
/// <summary>
/// 构造函数
/// </summary>
public TxSmsOutputDto()
{
Result = ; //默认为0,表示初始值或正确
Message = "";
} /// <summary>
/// 错误代码
/// </summary>
[JsonProperty("Result")]
public int Result { get; set; } /// <summary>
/// 错误信息
/// </summary>
[DisplayFormat(ConvertEmptyStringToNull = false)]
[JsonProperty("Message")]
public string Message { get; set; }
}
}
5.3 API接口
using System;
using System.Threading.Tasks;
using System.Web.Http;
using TxSms.Actions;
using TxSms.Inputs;
using TxSms.Outputs; namespace TxSms
{
/// <summary>
/// 二维码接口
/// </summary>
public class QRCodeController : TxSmsApiController
{
/// <summary>
/// 二维码登录认证
/// </summary>
/// <returns>
/// 0:登录成功;-1:参数错误 -2:ConnectionId、UUID、UserName、Password不允许为空-3:ConnectionId回话id不存在-4:UUID输入错误-5:UUID已过期
/// -6:本UUID已登录-7:登录账号已停用-8:登录账号已删除-9:登录密码输入错误-10:登录账号不存在
/// </returns>
[AllowAnonymous]
[HttpPost]
public async Task<TxSmsOutputDto> QRCodeVerify([FromBody]QRCodeVerifyInput model)
{
TxSmsOutputDto result = new TxSmsOutputDto(); #region 参数验证 if (model.IsNull())
{
result.Result = -;
result.Message = "参数错误";
return result;
}
if (model.ConnectionId.IsNullOrEmpty() || model.UUID.Equals(Guid.Empty) || model.UserName.IsNullOrEmpty() || model.Password.IsNullOrEmpty())
{
result.Result = -;
result.Message = "ConnectionId、UUID、UserName、Password不允许为空";
return result;
} #endregion 参数验证 #region 有效性判断 //验证ConnectionId合法性
if (QRCodeAction.QRCodeLists.ContainsKey(model.ConnectionId))
{
result.Result = -;
result.Message = "ConnectionId回话id不存在";
return result;
}
//验证UUID有效性
var findCode = QRCodeAction.QRCodeLists[model.ConnectionId];
if (!model.UUID.Equals(findCode.UUID))
{
result.Result = -;
result.Message = "UUID输入错误";
return result;
}
if (!findCode.IsValid())
{
result.Result = -;
result.Message = "UUID已过期";
return result;
}
if (findCode.IsLogin)
{
result.Result = -;
result.Message = "本UUID已登录";
return result;
} #endregion 有效性判断 LoginUserNameInput loginParam = new LoginUserNameInput
{
UserName = model.UserName,
Password = model.Password,
Platform = model.Platform
};
LoginOutput loginResult = await new SessionController().LoginUserName(loginParam);
switch (loginResult.Result)
{
case -:
result.Result = -;
result.Message = "登录账号已停用";
break; case -:
result.Result = -;
result.Message = "登录账号已删除";
break; case -:
result.Result = -;
result.Message = "登录密码输入错误";
break; case -:
result.Result = -;
result.Message = "登录账号不存在";
break;
}
if (loginResult.Result > ) //登录成功,值为AccId
{
result.Result = ;
findCode.IsLogin = true; //更改登录状态
result.Message = "成功登录";
}
return result;
}
}
}
6、疑难解答
6.1 #16解答
二维码中可以加入图片吗?
文中二维码 有个图片上面有 M 字母是怎么处理的?
第一个问题:是把存储图片信息存储到二维码中,手机扫码可以识别吧?这个问题涉及到二维码的存储容量,理论上如果二维码的存储容量足够大,可把图片序列化成01的字符进行存储,扫描就可以识别。但二维码有不同的标准,不同标准下数据容量是不同的。建议不要存储图片,详情可查看知乎,了解一下:http://www.zhihu.com/question/20387257
M字母是一个图片,来自http://www.dcloud.io/,只需要把想放的图放到已生成的二维码中间即可,但图片不宜过大,调试一下,用手机识别一下。有兴趣的朋友可以查看草榴二维码:http://cli.im/
6.2 #17解答
疑问: 输入参数有 用户名和密码,那个是每次都需要用户输入的?还是通过扫描二维码获得的? 还是哪种方式来给 输入参数的用户名和密码赋值的。
我想了解楼主是按哪种方式实现的呢?
首先要理解一下扫描登录的流程,【M】扫描二维码只获取相关【B】的唯一标识符信息,扫码之后,【M】(前提是【M】必须已经登录成功)发送用户名\密码\UUID到【S】进行一系列的验证;为了提高安全性,在【M】提交数据时,对密码进行md5时间戳加密。
6.3 #23解答
可以这样不 在手机端随机生成码 加密存在手机上并上传服务器 后端生成带有该码加时间的二维码 网页扫的时候对比登陆
要实现扫描登录,弄懂一个问题:为什么扫描二维码之后,提交给服务器的数据就是当前页面所需的呢?在本项目中,是通过SignalR的固有通信connectionid来确认的。你所说的流程应该如下:

在本流程图中,比方案中的步骤延长了;在Step2中,会出现问题,如何将【M】推送过来的UUID推送到你看到的【B】端?显然缺少纽带。本方法是不可行的。
7、总结与下载
二维码应用比较广泛,记得去北京的故宫旁边的中山公园,里面的古树也有二维码,扫描可查看相关联信息。紧紧对于二维码而言就是存储有限信息,但就是这有限的信息,可以将庞大的信息系统连接一起,所用的应用不是前沿技术的突破,而是我们思考问题方式的转变、思维角度的变化。由于二维码具有信息存储的独特性,可在以下方面应用:
- 信息获取(名片、地图、WIFI密码、资料)
- 网站跳转(跳转到微博、手机网站、网站)
- 广告推送(用户扫码,直接浏览商家推送的视频、音频广告)
- 手机电商(用户扫码、手机直接购物下单)
- 防伪溯源(用户扫码、即可查看生产地;同时后台可以获取最终消费地)
- 优惠促销(用户扫码,下载电子优惠券,抽奖)
- 会员管理(用户手机上获取电子会员信息、VIP服务)
- 手机支付(扫描商品二维码,通过银行或第三方支付提供的手机端通道完成支付)
由于最近在做短信业务平台,将二维码应用到营销管理中,每个业务人员具有独立的推广二维码,客户扫码可进行短信测试,若注册成为会员则就是本业务人员的直属客户,可查看《二维码在短信业务应用的初步构思》。
最后,上传《基于SignalR的消息推送与二维码描登录实现》主要文件下载:http://files.cnblogs.com/files/zsy/signalr%E4%B8%8Eqrcode.rar
基于SignalR的消息推送与二维码描登录实现的更多相关文章
- Android 基于Netty的消息推送方案之概念和工作原理(二)
上一篇文章中我讲述了关于消息推送的方案以及一个基于Netty实现的一个简单的Hello World,为了更好的理解Hello World中的代码,今天我来讲解一下关于Netty中一些概念和工作原理的内 ...
- Android 基于Netty的消息推送方案之字符串的接收和发送(三)
在上一篇文章中<Android 基于Netty的消息推送方案之概念和工作原理(二)> ,我们介绍过一些关于Netty的概念和工作原理的内容,今天我们先来介绍一个叫做ChannelBuffe ...
- AngularJS+ASP.NET MVC+SignalR实现消息推送
原文:AngularJS+ASP.NET MVC+SignalR实现消息推送 背景 OA管理系统中,员工提交申请单,消息实时通知到相关人员及时进行审批,审批之后将结果推送给用户. 技术选择 最开始发现 ...
- Signalr实现消息推送
一.前言 大多数系统里面好像都有获取消息的功能,但这些消息来源都不是实时的,比如你开两个浏览器,用两个不同的账号登录,用一个账号给另外一个账号发送消息,然而并不会实时收到消息,必须要自己手动F5刷新一 ...
- Android 基于Netty的消息推送方案之对象的传递(四)
在上一篇文章中<Android 基于Netty的消息推送方案之字符串的接收和发送(三)>我们介绍了Netty的字符串传递,我们知道了Netty的消息传递都是基于流,通过ChannelBuf ...
- Knative 实战:基于 Kafka 实现消息推送
作者 | 元毅 阿里云智能事业群高级开发工程师 导读:当前在 Knative 中已经提供了对 Kafka 事件源的支持,那么如何基于 Kafka 实现消息推送呢?本文作者将以阿里云 Kafka 产品为 ...
- 在云平台上基于Go语言+Google图表API提供二维码生成应用
二维码能够说已经深深的融入了我们的生活其中.到处可见它的身影:但通常我们都是去扫二维码, 曾经我们分享给朋友一个网址直接把Url发过去,如今我们能够把自己的信息生成二维码再分享给他人. 这里就分享一下 ...
- 二维码闪电登录流程详解,附demo(1/2)
二维码,最早发明于日本,它是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设 ...
- 实现手机扫描二维码页面登录,类似web微信-第一篇,业务分析
转自:http://www.cnblogs.com/fengyun99/p/3541249.html 关于XMPP组件的文章,先休息两天,好歹已经完整的写了一份. 这两天,先实现一套关于web微信扫描 ...
随机推荐
- bootstrap-datetimepicker 进一步跟进~~~开始时间和结束时间的样式显示
上次简单介绍了一下:05.LoT.UI 前后台通用框架分解系列之——漂亮的时间选择器(http://www.cnblogs.com/dunitian/p/5524019.html) 这次深入再介绍一下 ...
- HTML DOM 对象
本篇主要介绍HTML DOM 对象:Document.Element.Attr.Event等4个对象. 目录 1. Document 对象:表示文档树的根节点,大部分属性和方法都是对元素进行操作. 2 ...
- 页面中多个script块之间的关系
一:函数声明与函数定义表达式在函数调用间的区别 <script type="text/javascript"> doA(); var doA = function(a ...
- 伪共享(false sharing),并发编程无声的性能杀手
在并发编程过程中,我们大部分的焦点都放在如何控制共享变量的访问控制上(代码层面),但是很少人会关注系统硬件及 JVM 底层相关的影响因素.前段时间学习了一个牛X的高性能异步处理框架 Disruptor ...
- C#多线程之线程池篇1
在C#多线程之线程池篇中,我们将学习多线程访问共享资源的一些通用的技术,我们将学习到以下知识点: 在线程池中调用委托 在线程池中执行异步操作 线程池和并行度 实现取消选项 使用等待句柄和超时 使用计时 ...
- HDU1671——前缀树的一点感触
题目http://acm.hdu.edu.cn/showproblem.php?pid=1671 题目本身不难,一棵前缀树OK,但是前两次提交都没有成功. 第一次Memory Limit Exceed ...
- Spark踩坑记——初试
[TOC] Spark简介 整体认识 Apache Spark是一个围绕速度.易用性和复杂分析构建的大数据处理框架.最初在2009年由加州大学伯克利分校的AMPLab开发,并于2010年成为Apach ...
- 深入Java虚拟机--判断对象存活状态
程序计数器,虚拟机栈和本地方法栈 首先我们先来看下垃圾回收中不会管理到的内存区域,在Java虚拟机的运行时数据区我们可以看到,程序计数器,虚拟机栈,本地方法栈这三个地方是比较特别的.这个三个部分的特点 ...
- Javascript 严格模式详解
转自http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html 一.概述 除了正常运行模式,ECMAscript 5添加了第二 ...
- 手机web如何实现多平台分享
话说App一般都带有分享到社交平台的入口,web网页的分享也有很不错的框架,但是随着HTML5的不断发展,手机web页面越来越多的进入到我们的生活中,那如何在我们的手机上完成分享呢?话说各大分享平台都 ...

