.NET SignalR2持久连接层解析

 

越是到年底越是感觉浑身无力,看着啥也不想动,只期盼着年终奖的到来以此来给自己打一针强心剂。估摸着大多数人都跟我一样犯着这样浑身无力的病,感觉今年算是没挣到啥钱,但是话也不能这么说,搞得好像去年挣到钱了似的。不过该做的事情还是得到,因为毕竟自己拿着工资,拿一天钱就得干一天活,无论自己觉得工资给的多还是少,觉得少可以辞职不干,拿着工资就证明自己愿意接受,既然愿意接受,那就没有理由不努力去干活,我觉得这是作为一个员工最起码的信念。

隔着有一段时间没写博客了,反正最近各种乱七八糟的事都在发生,不过不管怎么样,事情总会慢慢过去。(最近没事,正在研究神学,待有所感悟再与大家分享。)

这一篇博客主要介绍一下ASP.NET SignalR持久连接层的相关内容,如有写的不到位和错误的地方,还望大家多多指正。

一.ASP.NET SignalR概述:

谈到ASP.NET SignalR大多数人应该会比较的熟悉,因在我们的mvc项目中,使用到ASP.NET SignalR的地方还是比较多的。

ASP.NET SignalR是ASP.NET开发人员的库,它简化了向应用程序添加实时Web功能的过程。实时网络功能能够让服务器代码在连接的客户端可用时立即将内容推送到连接的客户端,而不是让服务器等待客户端请求新数据。SignalR可用于向ASP.NET应用程序添加任何类型的“实时”Web功能。任何时候用户刷新网页,看看新的数据或页面实现长轮询来获取新的数据,它是使用SignalR的候选人。示例包括仪表板和监视应用程序,协作应用程序(如同时编辑文档),作业进度更新和实时表单。

SignalR还支持需要来自服务器的高频更新的全新类型的web应用。SignalR自动处理连接管理,并允许您同时向所有连接的客户端广播消息。SignalR支持“服务器推送”功能,其中服务器代码可以使用远程过程调用(RPC)在浏览器中调用客户端代码。SignalR的应用可以扩展到使用服务总线。

SignalR提供了一个用于创建从服务器端.NET代码调用客户端浏览器(和其他客户端平台)中的JavaScript函数的服务器到客户端远程过程调用(RPC)的简单API。SignalR还包括用于连接管理(例如,连接和断开事件)的API和分组连接。SignalR提供了一个用于创建从服务器端.NET代码调用客户端浏览器(和其他客户端平台)中的JavaScript函数的服务器到客户端远程过程调用(RPC)的简单API。SignalR还包括用于连接管理(例如,连接和断开事件)的API和分组连接。

(以上描述摘自MSDN)

上面介绍了ASP.NET SignalR的一些基本概念和操作,接下来看一下ASP.NET SignalR的抽象层,由底层向上以此为:Internet协议,传输(WebSockets,Server-SentEvents,foreverFrame,长轮询),持久连接层,Hub层。

由以上的结构图可知ASP.NET SignalR的抽象层结构,在服务器端,当连接打开或关闭、接收数据、给客户端发送信息时,将接受到通知;在客户端,打开或关闭连接,发送或接收任何数据。在ASP.NET SignalR的持久连接层中,有一个核心对象:PersisterConnection类,接下来我们具体了解一下这个类的一些方法。

二.ASP.NET SignalR持久连接层服务端核心对象方法解析:

ASP.NET SignalR中的每一个持久层都可以通过某一个URL从外部进行访问。为保持客户端和服务器之间持久连接的开放性,并使用传输在这样的连接上发送数据,这个用来访问SignalR持久连接的底层API提供了隐藏固有复杂性的抽象层。

1.PersisterConnection类的事件方法:

在PersisterConnection中包含几个事件方法,这几个方法都是虚方法,如下:

    OnConnected():在建立新连接时调用。

    OnReconnected():在超时后连接重新连接时调用。

    OnReceived():从连接接收数据时调用。

    OnDisconnected():当连接正常断开或由于超时时调用。

以上的4个方法中,返回的类型都是TaskAsyncHelper.Empty,如下源码:

    /// <summary>
/// 从连接接收数据时调用。
/// </summary>
/// <param name="request">当前连接的IRequest。</param>
<param name="connectionId">发送数据的连接的ID。</param>
<param name="data">有效负载发送到连接。</param>
/// <returns>
///System.Threading.Tasks.Task在接收操作完成时完成。/// </returns>
protected virtual Task OnReceived(IRequest request, string connectionId, string data)
{
return TaskAsyncHelper.Empty;
}

以上的方法都是异步方法,用以实现代码的异步执行,或者返回一个能够通过某个Task对象表示和异步执行的后台任务,在方法的传入参数中,代码中已经做类对应的介绍。如果需要进一步了解TaskAsyncHelper.Empty的相关信息,可以具体查看System.Threading.Tasks.Task命名空间中的类。

2.ProcessRequest():处理所有的请求。

PersisterConnection类的ProcessRequest()方法是用与OWIN入口点,该方法存在多个重载版本,现在只介绍一个重载的源码:

    public virtual Task ProcessRequest(HostContext context)
{
if (context == null)
throw new ArgumentNullException("context");
if (!this._initialized)
throw new InvalidOperationException(string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.Error_ConnectionNotInitialized, new object[0]));
if (PersistentConnection.IsNegotiationRequest(context.Request))
return this.ProcessNegotiationRequest(context);
if (PersistentConnection.IsPingRequest(context.Request))
return PersistentConnection.ProcessPingRequest(context);
this.Transport = this.GetTransport(context);
if (this.Transport == null)
return PersistentConnection.FailResponse(context.Response, string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.Error_ProtocolErrorUnknownTransport, new object[0]), 400);
string connectionToken = context.Request.QueryString["connectionToken"];
if (string.IsNullOrEmpty(connectionToken))
return PersistentConnection.FailResponse(context.Response, string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.Error_ProtocolErrorMissingConnectionToken, new object[0]), 400);
string connectionId;
string message;
int statusCode;
if (!this.TryGetConnectionId(context, connectionToken, out connectionId, out message, out statusCode))
return PersistentConnection.FailResponse(context.Response, message, statusCode);
this.Transport.ConnectionId = connectionId;
Task<string> groupsToken = this.Transport.GetGroupsToken();
Func<string, PersistentConnection, HostContext, Task> func = (Func<string, PersistentConnection, HostContext, Task>) ((g, pc, c) => pc.ProcessRequestPostGroupRead(c, g));
HostContext hostContext = context;
Func<string, PersistentConnection, HostContext, Task> successor;
return TaskAsyncHelper.FastUnwrap(TaskAsyncHelper.Then<string, PersistentConnection, HostContext, Task>(groupsToken, successor, this, hostContext));
}

由以上的源码可以看出,该方法为一个异步虚方法,并且接受一个参数HostContext表示当前请求。当一个PersistentConnection的管道完成时,返回一个System.Threading.Tasks.Task。Transport.GetGroupsToken()用与获取组令牌。ProcessRequest()的另一个重载版本是OWIN的入口。

3.TryGetConnectionId():用与获取ConnectionId。

看到ConnectionId应该都不会陌生,因为在前面介绍的4中事件方法中有一个参数就是ConnectionId,该参数是一个唯一标识符,他和初始化通信过程中通过SiganlR自动产生的连接有关。默认情况下,SignalR框架将使用一个guid进行标识。

可以使用该连接符给某些特定的客户端直接发送消息,或是对他们实施任何类型的个性化监视。

接下来,我们具体看一下源码:

internal bool TryGetConnectionId(HostContext context, string connectionToken, out string connectionId, out string message, out int statusCode)
{
string str1 = (string) null;
connectionId = (string) null;
message = (string) null;
statusCode = 400;
try
{
str1 = this.ProtectedData.Unprotect(connectionToken, "SignalR.ConnectionToken");
}
catch (Exception ex)
{
TraceSource trace = this.Trace;
string format = "Failed to process connectionToken {0}: {1}";
object[] objArray = new object[2];
int index1 = 0;
string str2 = connectionToken;
objArray[index1] = (object) str2;
int index2 = 1;
Exception exception = ex;
objArray[index2] = (object) exception;
trace.TraceInformation(format, objArray);
}
if (string.IsNullOrEmpty(str1))
{
message = string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.Error_ConnectionIdIncorrectFormat, new object[0]);
return false;
}
string[] strArray = str1.Split(PersistentConnection.SplitChars, 2);
connectionId = strArray[0];
if (string.Equals(strArray.Length > 1 ? strArray[1] : string.Empty, PersistentConnection.GetUserIdentity(context), StringComparison.OrdinalIgnoreCase))
return true;
message = string.Format((IFormatProvider) CultureInfo.CurrentCulture, Resources.Error_UnrecognizedUserIdentity, new object[0]);
statusCode = 403;
return false;
}

该方法返回bool值,接受5个参数,分别为:HostContext请求内容,connectionToken连接令牌,connectionId连接ID,message消息,statusCode状态代码。进入方法后,首先判断参数信息是否符合要求。ProtectedData.Unprotect()方法用与取消保护,接受传入的连接令牌。PersistentConnection.GetUserIdentity()用与获取用户身份。

4.VerifyGroups():用与验证组。

在我们的实际项目中,一般是针对某一个用户进行消息的处理,如果想要将消息按照组别进行操作应该怎么处理,在SignalR提供了一个VerifyGroups方法。

 internal IList<string> VerifyGroups(string connectionId, string groupsToken)
{
if (string.IsNullOrEmpty(groupsToken))
return ListHelper<string>.Empty;
string str1 = (string) null;
try
{
str1 = this.ProtectedData.Unprotect(groupsToken, "SignalR.Groups.v1.1");
}
catch (Exception ex)
{
TraceSource trace = this.Trace;
string format = "Failed to process groupsToken {0}: {1}";
object[] objArray = new object[2];
int index1 = 0;
string str2 = groupsToken;
objArray[index1] = (object) str2;
int index2 = 1;
Exception exception = ex;
objArray[index2] = (object) exception;
trace.TraceInformation(format, objArray);
}
if (string.IsNullOrEmpty(str1))
return ListHelper<string>.Empty;
string[] strArray = str1.Split(PersistentConnection.SplitChars, 2);
string a = strArray[0];
string json = strArray.Length > 1 ? strArray[1] : string.Empty;
string b = connectionId;
int num = 5;
if (!string.Equals(a, b, (StringComparison) num))
return ListHelper<string>.Empty;
return (IList<string>) JsonSerializerExtensions.Parse<string[]>(this.JsonSerializer, json);
}

该方法返回一个List<string>集合,接受两个参数connectionId,groupsToken进行分组操作。ProtectedData.Unprotect()方法用与取消保护,接受传入的连接令牌。在SignalR中一般在处理消息请求时,需要进行取消保护这一步操作。Split()对取消保护操作后返回的数据进行分割获取一个数组。

三.总结:

以上是简单的介绍SignalR的持久层的一些方法,并没有提供一些基本样例,因为个人觉得在网上还是有自己多的demo,微软的官网也提供的更为详尽的操作说明,所以在这里就不做这一方面的重复介绍。

NET SignalR2的更多相关文章

  1. 脑洞大开之采用HTML5+SignalR2.0(.Net)实现原生Web视频

    目录 对SignalR不了解的人可以直接移步下面的目录 SignalR系列目录 前言 - -,我又来了,今天废话不多说,我们直接来实现Web视频聊天. 采用的技术如下: HTML5 WebRTC Si ...

  2. SignalR2.0开发实例之——设置时间、后台其他地方使用集线器、使用自己的连接ID

    一.连接的生命周期设置: 如下: // 该值表示连接在超时之前保持打开状态的时间长度. //默认为110秒 GlobalHost.Configuration.ConnectionTimeout = T ...

  3. SignalR2.0开发实例之——创建房间聊天

    SignalR作为一个强大的集线器,已经在hub里面集成了Gorups,也就是分组管理,使用方法如下: //作用:将连接ID加入某个组 //Context.ConnectionId 连接ID,每个页面 ...

  4. SignalR2.0开发实例之——私聊

    一.前言 继续上一章的补充,这章介绍使用私聊的功能.主要通过一个方法   Clients.Client(Context.ConnectionId).showMessage(msg); SignalR框 ...

  5. SignalR2.0开发实例之——群发消息

    一.前言 ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信.什么是实时通信的Web呢?就是让客户端(Web页面)和服务器端可以互相 ...

  6. MVC5中使用SignalR2.0实现实时聊天室

    原文 MVC5中使用SignalR2.0实现实时聊天室 有时候需要浏览器和服务端保持实时的通讯(比如在线聊天),SignalR的出现让这一切变得非常简单.它能够让服务端向客户端实时的推送消息.如果用户 ...

  7. [SignalR2] 认证和授权

    SignalR自身不提供任何用户认证特征,相反,是直接使用现有且基于(Claims-based)声明认证系统(关于这方面知识详见参考资料),非常明了,不解释,看代码中的验证代码: protected ...

  8. SignalR2结合ujtopo实现拓扑图动态变化

    上一篇文章基于jTopo的拓扑图设计工具库ujtopo,介绍了拓扑设计工具,这一篇我们使用SignalR2结合ujtopo实现拓扑图的动态变化. 仅仅作为演示,之前的文章SignalR2简易数据看板演 ...

  9. SignalR2简易数据看板演示

    软件环境: 1.vs2015.windows7..net4.5 演示说明: 当点击按钮的时候,柱状图数值加1并实时变化 1.首先打开vs2015创建一个mvc项目,并安装SignalR2,具体操作可参 ...

  10. Asp.Net Mvc5 结合 SignalR2.0+ 实现消息交互简单例子

    Nuget添加 SignalR包 1.构建一个MyHubServer服务端类 定义类的特性标签为 [HubName("chatHub")] public class MyHubSe ...

随机推荐

  1. 利用Eclipse+openJTAG调试led.axf文件

    转自calvinlee1984 Subject:利用Eclipse+openJTAG调试led.axf文件 Date:     3-Mar-2011 By:         Calvinlee1984 ...

  2. [Debug] Chrome Devtools: Elements - Console Integration

    The Element Inspector in Chrome DevTools offers powerful integration with the console - learn how to ...

  3. [AngularFire2] Pagination

    Let's see how to do pagination in Firebase: For the init loading, we only want 3 items: findLessonsK ...

  4. 怎样自己构建一个小型的Zoomeye----从技术细节探讨到实现

     转载请注明出处:viewmode=list">http://blog.csdn.net/u011721501?viewmode=list 0.概述 Zoomeye是个网络空间的搜 ...

  5. POJ 1511 Invitation Cards (ZOJ 2008) 使用优先队列的dijkstra

    传送门: http://poj.org/problem?id=1511 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1008 ...

  6. 3、Pycharm使用

    1.设置文件模板 file->settings->Editor->File and Code Templates->Python Script 2.运行 a.点击要运行的文件, ...

  7. linux 安装完mysql 密码重置

    If you have forgot the MySQL root password, can’t remember or want to break in….. you can reset them ...

  8. 【u251】心灵的抚慰

    Time Limit: 1 second Memory Limit: 128 MB [问题描述] 病毒问题解决后,神牛们的心灵久久不能平静.他可以从一个程序联想到一些相似的程序.比如从程序1联想到2, ...

  9. 每天一个JavaScript实例-操作元素定位元素

    <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...

  10. iOS开发项目实战——Swift实现ScrollView滚动栏功能

    手机作为一个小屏设备,须要显示的信息往往无法在一个屏幕上显示,此时就须要使用到滚动栏,当然除了像TableView这样能够自带滚动功能的. 假设一个界面上View较多,那就必须要使用到ScrollVi ...