前言
以前在培训ASP.NET WebAPI的时候有提过SignalR这个技术,但当时只是讲了是用来作什么的,并无多说。由于本身也是画图找资料的时候见到的。后来当一直关注的前端大神贤心发布LayIM2.0以后,因而对Web聊天产生了兴趣。那么在.NET平台下的Web聊天有哪些呢?查找资料发现了ASP.NET SignalR。因而乎...So...Just do it!javascript
简介
按照惯例,先介绍一下什么是SignalR。简单的说,ASP .NET SignalR 是一个ASP .NET 下的类库,能够在ASP .NET 的Web项目中实现实时通讯。
什么是实时通讯的Web呢?就是让客户端(Web页面)和服务器端能够互相通知消息及调用方法,固然这是实时操做的。
WebSockets是HTML5提供的新的API,能够在Web网页与服务器端间创建Socket链接,当WebSockets可用时(即浏览器支持Html5)SignalR使用WebSockets,当不支持时SignalR将使用其它技术来保证达到相同效果。
SignalR固然也提供了很是简单易用的高阶API,使服务器端能够单个或批量调用客户端上的JavaScript函数,而且很是 方便地进行链接管理,例如客户端链接到服务器端,或断开链接,客户端分组,以及客户端受权,使用SignalR都很是 容易实现。
做用
SignalR 将与客户端进行实时通讯带给了ASP .NET 。固然这样既好用,并且也有足够的扩展性。之前用户须要刷新页面或使用Ajax轮询才能实现的实时显示数据,如今只要使用SignalR,就能够简单实现了。
最重要的是您无需从新创建项目,使用现有ASP .NET项目便可无缝使用SignalR。html
Owin规范
OWIN是Open Web Server Interface for .NET的首字母缩写,他的定义以下:
OWIN在.NET Web Servers与Web Application之间定义了一套标准接口,OWIN的目标是用于解耦Web Server和Web Application。基于此标准,鼓励开发者开发简单、灵活的模块,从而推动.NET Web Development开源生态系统的发展。
正如你看到的这样,OWIN是接口、契约,而非具体的代码实现,仅仅是规范(specifications),因此要实现自定义基于OWIN的Web Server必需要实现此规范。
历时两年(2010-2012),OWIN的规范终于完成而且当前版本是1.0,在OWIN的官网上能够看到更具体的信息。
实际上,OWIN的规范很是简单,他定义了一系列的层(Layer),而且他们的顺序是以堆(Stack)的形式定义,以下所示。OWIN中的接口被称之为应用程序委托或者AppFunc,用来在这些层之间通讯。前端
OWIN定义了4层:
Host:主要负责应用程序的配置和启动进程,包括初始化OWIN Pipeline、运行Server。
Server:绑定套接字并监听的HTTP请求而后将Request和Response的Body、Header封装成符合OWIN规范的字典并发送到OWIN Middleware Pipeline中
Middleware:称之为中间件、组件,位于Server与Application之间,用来处理发送到Pipeline中的请求
Application:这是具体的应用程序代码,只不过咱们将他们注册到OWIN Pipeline中去处理HTTP 请求,成为OWIN管道的一部分
Application Delegate
OWIN规范另外一个重要的组成部分是接口的定义,用于Server和Middleware的交互。他并非严格意义上的接口,而是一个委托而且每一个OWIN中间件组件必须提供。
详见:http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-owin.html
Katana
微软引入并推广OWIN,同时依照OWIN规范,实现了Katana。Host.SystemWeb, Host.HttpListener 都是Katana的一个组件。
Katana的基本原则
● 可移植性:从HostàServeràMiddleware,每一个Pipeline中的组件都是可替换的,而且第三方公司和开源项目的Framework都是能够在OWIN Server上运行,也就是说不受平台限制,从而实现跨平台。
● 模块化:每个组件都必须保持足够独立性,一般只作一件事,以混合模块的形式来知足实际的开发需求
● 轻量和高效:由于每个组件都是模块化开发,并且能够轻松的在Pipeline中插拔组件,实现高效开发
Katana 体系结构
Katana实现了OWIN的Layers,因此Katana的体系结构和OWIN一致,以下所示:java
1.)Host :宿主Host被OWIN规范定义在第一层(最底层),他的职责是管理底层的进程(启动、关闭)、初始化OWIN Pipeline、选择Server运行等。
Katana为咱们提供了3中选择:
● IIS / ASP.NET :使用IIS是最简单和向后兼容方式,在这种场景中OWIN Pipeline经过标准的HttpModule和HttpHandler启动。使用此Host你必须使用System.Web做为OWIN Server
● Custom Host :若是你想要使用其余Server来替换掉System.Web,而且能够有更多的控制权,那么你能够选择建立一个自定义宿主,如使用Windows Service、控制台应用程序、Winform来承载Server。
● OwinHost :若是你对上面两种Host还不满意,那么最后一个选择是使用Katana提供的OwinHost.exe:他是一个命令行应用程序,运行在项目的根部,启动HttpListener Server并找到基于约束的Startup启动项。OwinHost提供了命令行选项来自定义他的行为,好比:手动指定Startup启动项或者使用其余Server(若是你不须要默认的HttpListener Server)。
2.)Server
Host以后的Layer被称为Server,他负责打开套接字并监听Http请求,一旦请求到达,根据Http请求来构建符合OWIN规范的Environment Dictionary(环境字典)并将它发送到Pipeline中交由Middleware处理。Katana对OWIN Server的实现分为以下几类:
● System.Web:如前所述那样,System.Web和IIS/ASP.NET Host二者彼此耦合,当你选择使用System.Web做为Server ,Katana System.Web Server把本身注册为HttpModule和HttpHandler而且处理发送给IIS的请求,最后将HttpRequest、HttpResponse对象映射为OWIN环境字典并将它发送至Pipeline中处理。
● HttpListener:这是OwinHost.exe和自定义Host默认的Server。
● WebListener:这是ASP.NET vNext默认的轻量级Server,他目前没法使用在Katana中
3)Middleware
Middleware(中间件)位于Host、Server以后,用来处理Pipeline中的请求,Middleware能够理解为实现了OWIN应用程序委托AppFun的组件。
Middleware处理请求以后并能够交由下一个Pipeline中的Middleware组件处理,即链式处理请求,经过环境字典能够获取到全部的Http请求数据和自定义数据。Middleware能够是简单的Log组件,亦能够为复杂的大型Web Framework,诸如:ASP.NET Web API、Nancy、SignlR等,以下图所示:Pipeline中的Middleware用来处理请求:jquery
4.)Application
最后一层即为Application,是具体的代码实现,好比ASP.NET Web API、SignalR具体代码的实现。浏览器
详见:http://www.cnblogs.com/OceanEyes/p/thinking-in-asp-net-mvc-what-is-katana.html服务器
说了那么多,接下来开始上代码。并发
开始以前,准备工做:必须安装.NET Framework 4.5 , Visual Studio 2013+,VS2012须要安装其余软件和配置,也可能会出现一些位置问题(我用的VS2015)mvc
持久链接:
一、新建一个空项目asp.net
二、新建一个OWIN Startup类,名称随意,这里以Startup为例。
三、新建SignalR永久链接类,名称随意,这里以MyConnection为例。
当该类添加完成后,会自动添加SignalR的相关引用和Script(如图所示)
四、打开Startup.cs添加对MyConnection的映射
五、新建一个HTML页面,名称随意。在新建的HTML页面上引用jquery-1.10.2.js和jquery.signalR-2.1.2.min.js
六、与服务端通信,写入下图红框内的代码
打开浏览器,F12打开控制台,如图显示,说明链接成功。
为何会是输出Welcome呢?
$.connection("/myconn");建立了一个链接,链接是哪儿呢,“/myconn”就是以前在Startup类中定义的MyConnection中映射路径。当创建链接后,调用OnConnected方法,以下:
如今来进行消息发送
1 var conn = $.connection("/myconn");//建立链接
2 //开始链接
3 conn.start(function () { 4 console.log("已链接"); 5 }).done(function() { 6 conn.send("你好!"); 7 }); 8 //接受消息
9 conn.received(function (data) { 10 console.log(data); 11 });
SignalR.js提供了发送接口send() <注意:start()方法是异步方法,send()只能在回调函数中执行,或者自定义参数标识。>
那么服务端是怎么接收的呢?
仍是在MyConnection中,此处将你发送的信息给广播出去
下边用持久链接类来演示一个简单的聊天室的功能,只是简单实现功能,只作演示,勿喷。
直接上代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Threading.Tasks; 5 using System.Web; 6 using Microsoft.AspNet.SignalR; 7 using Newtonsoft.Json; 8 9 namespace SignalRDemo 10 { 11 public class MyConnection : PersistentConnection 12 { 13 14 /// <summary> 15 /// 创建新链接时调用 16 /// </summary> 17 /// <param name="request">当前链接的请求</param> 18 /// <param name="connectionId">进行从新链接的客户端的 ID。</param> 19 /// <returns>链接操做完成时将完成的 System.Threading.Tasks.Task。 </returns> 20 protected override Task OnConnected(IRequest request, string connectionId) 21 { 22 return Connection.Send(connectionId, "Welcome!"); 23 } 24 25 /// <summary> 26 /// 从链接接收数据时调用 27 /// </summary> 28 /// <param name="request">当前链接的请求</param> 29 /// <param name="connectionId"> 发送数据的链接的 ID。</param> 30 /// <param name="data">发送到链接的负载。</param> 31 /// <returns>接收操做完成时将完成的 System.Threading.Tasks.Task。</returns> 32 protected override Task OnReceived(IRequest request, string connectionId, string data) 33 { 34 var mode = JsonConvert.DeserializeObject<MyChart>(data); 35 if (mode.Action == "welcome") 36 { 37 this.Groups.Add(connectionId, mode.RoomName); 38 39 //除了当前这我的,该房间的其余人都会受到welcom new user的消息。 40 return this.Groups.Send(mode.RoomName, string.Format("欢迎新用户进入房间: {0}", mode.RoomName), connectionId); 41 } 42 else 43 { 44 //发送的消息,排除当前这我的 45 return this.Groups.Send(mode.RoomName, string.Format("用户{0}说:{1}", connectionId, mode.Data), connectionId); 46 } 47 } 48 } 49 50 51 public class MyChart 52 { 53 public string RoomName { get; set; } 54 public string Data { get; set; } 55 public string Action { get; set; } 56 57 } 58 }
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <title></title> 6 <meta charset="utf-8" /> 7 <script src="Scripts/jquery-1.10.2.js"></script> 8 <script src="Scripts/jquery.signalR-2.1.2.min.js"></script> 9 10 </head> 11 <body> 12 <h1>SignalR</h1> 13 <input type="button" value="房间一" id="room1" /> 14 <input type="text" id="msg" /> 15 <input type="button" value="发送" id="send" /> 16 <script type="text/javascript"> 17 $(function () { 18 var conn = $.connection("/myconn"); 19 //conn.logging = true; 20 var isconn = false; 21 conn.start(function () { 22 isconn = true; 23 }); 24 25 $("#room1").click(function () { 26 if (isconn) { 27 conn.send({ RoomName: "room1", Action: "welcome" }); 28 } 29 }); 30 $("#send").click(function () { 31 if (isconn) { 32 conn.send({ 33 RoomName: "room1", 34 Action: "2", 35 Data: $("#msg").val() 36 }); 37 } 38 39 }); 40 conn.connectionSlow(function (data) { 41 42 }); 43 conn.disconnected(function (data) { 44 45 }); 46 conn.error(function (data) { }); 47 conn.received(function (data) { 48 console.log(data); 49 }); 50 }); 51 52 </script> 53 </body> 54 </html>
效果以下:
以上咱们都是在“持久链接”层上面作的操做,相似socket这样的模式。。。咱们真正有用的操做是由一个OnReceived方法。
这样看起来,是否是很单一?并且也很是不人性化,那么为了解决这种问题,singlaR给咱们封装了更高一层的应用。
那么这个就叫作Hub,这个Hub使用的是相似于RPC的调用模式。
关于Hub,将会在下一篇文章中来说细细讲解。
官方教程https://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr