一、前言

  之前半年时间感觉自己有点浮躁,导致停顿了半年多的时间没有更新博客,今天重新开始记录博文,希望自己可以找回初心,继续沉淀。由于最近做的项目中用到SignalR技术,所以打算总结下Asp.net SignalR的相关内容,希望对刚接触或者接触不多的朋友有所帮助。今天的专题就是让大家可以快速的上手Asp.net SignalR。废话不多说了,下面正式进入今天专题的内容。

二、Asp.net SignalR 是个什么东东

  Asp.net SignalR是微软为实现实时通信的一个类库。一般情况下,SignalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,SignalR也支持WebSockets通信。另外SignalR开发的程序不仅仅限制于宿主在IIS中,也可以宿主在任何应用程序,包括控制台,客户端程序和Windows服务等,另外还支持Mono,这意味着它可以实现跨平台部署在Linux环境下。

  SignalR内部有两类对象:

  1. Http持久连接(Persisten Connection)对象:用来解决长时间连接的功能。还可以由客户端主动向服务器要求数据,而服务器端不需要实现太多细节,只需要处理PersistentConnection 内所提供的五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。
  2. Hub(集线器)对象:用来解决实时(realtime)信息交换的功能,服务端可以利用URL来注册一个或多个Hub,只要连接到这个Hub,就能与所有的客户端共享发送到服务器上的信息,同时服务端可以调用客户端的脚本。

  SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。

  客户端和服务端的具体交互情况如下图所示:

  从上面的介绍可以看出,SignalR既然是为实时而生的,这样就决定了其使用场所。具体适用情景有如下几点:

  1. 聊天室,如在线客服系统,IM系统等
  2. 股票价格实时更新
  3. 消息的推送服务
  4. 游戏中人物位置的实时推送

  目前,我所在公司在开发的就是在线客服系统。

三、使用Asp.net SignalR在Web端实现广播消息

  通过第二部分的介绍,相信大家对Asp.net SignalR有了一个初步的了解,接下来通过两个例子来让大家加深对SignalR运行机制的理解。第一个例子就是在Web端如何使用SignalR来实现广播消息。

  1. 使用Visual Studio 2013,创建一个MVC工程
  2. 通过Nuget安装SignalR包。右键引用-》选择管理Nuget程序包-》在出现的窗口中输入SignalR来找到SignalR包进行安装。
  3. 安装SignalR成功后,SignalR库的脚本将被添加进Scripts文件夹下。具体如下图所示:

  4. 向项目中添加一个SignalR集线器(v2)并命名为ServerHub。

  5. 将下面代码填充到刚刚创建的ServerHub类中。

  1. using Microsoft.AspNet.SignalR;
  2. using Microsoft.AspNet.SignalR.Hubs;
  3. using System;
  4.  
  5. namespace SignalRQuickStart
  6. {public class ServerHub : Hub
  7. {
  8. private static readonly char[] Constant =
  9. {
  10. '', '', '', '', '', '', '', '', '', '',
  11. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  12. 'w', 'x', 'y', 'z',
  13. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
  14. 'W', 'X', 'Y', 'Z'
  15. };
  16.  
  17. /// <summary>
  18. /// 供客户端调用的服务器端代码
  19. /// </summary>
  20. /// <param name="message"></param>
  21. public void Send(string message)
  22. {
  23. var name = GenerateRandomName();
  24.  
  25. // 调用所有客户端的sendMessage方法
  26. Clients.All.sendMessage(name, message);
  27. }
  28.  
  29. /// <summary>
  30. /// 产生随机用户名函数
  31. /// </summary>
  32. /// <param name="length">用户名长度</param>
  33. /// <returns></returns>
  34. public static string GenerateRandomName(int length)
  35. {
  36. var newRandom = new System.Text.StringBuilder();
  37. var rd = new Random();
  38. for (var i = ; i < length; i++)
  39. {
  40. newRandom.Append(Constant[rd.Next()]);
  41. }
  42. return newRandom.ToString();
  43. }
  44. }
  45. }

  6. 创建一个Startup类,如果开始创建MVC项目的时候没有更改身份验证的话,这个类会默认添加的,如果已有就不需要重复添加了。按照如下代码更新Startup类。

  7. 在Home控制器中创建一个Home Action方法

  1. public class HomeController : Controller
  2. {
  3. public ActionResult Index()
  4. {
  5. return View();
  6. }
  7.  
  8. public ActionResult About()
  9. {
  10. ViewBag.Message = "Your application description page.";
  11.  
  12. return View();
  13. }
  14.  
  15. public ActionResult Contact()
  16. {
  17. ViewBag.Message = "Your contact page.";
  18.  
  19. return View();
  20. }
  21.  
  22. public ActionResult Chat()
  23. {
  24. return View();
  25. }
  26. }

  8. 在Views文件中Home文件夹中创建一个Chat视图,视图代码如下所示:

  1. @{
  2. ViewBag.Title = "聊天窗口";
  3. }
  4.  
  5. <h2>Chat</h2>
  6.  
  7. <div class="container">
  8. <input type="text" id="message" />
  9. <input type="button" id="sendmessage" value="Send" />
  10. <input type="hidden" id="displayname" />
  11. <ul id="discussion"></ul>
  12. </div>
  13.  
  14. @section scripts
  15. {
  16. <!--引用SignalR库. -->
  17. <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
  18. <!--引用自动生成的SignalR 集线器(Hub)脚本.在运行的时候在浏览器的Source下可看到 -->
  19. <script src="~/signalr/hubs"></script>
  20.  
  21. <script>
  22. $(function () {
  23. // 引用自动生成的集线器代理
  24. var chat = $.connection.serverHub;
  25. // 定义服务器端调用的客户端sendMessage来显示新消息
  26.  
  27. chat.client.sendMessage = function (name, message) {
  28. // 向页面添加消息
  29. $('#discussion').append('<li><strong>' + htmlEncode(name)
  30. + '</strong>: ' + htmlEncode(message) + '</li>');
  31. };
  32.  
  33. // 设置焦点到输入框
  34. $('#message').focus();
  35. // 开始连接服务器
  36. $.connection.hub.start().done(function () {
  37. $('#sendmessage').click(function () {
  38. // 调用服务器端集线器的Send方法
  39. chat.server.send($('#message').val());
  40. // 清空输入框信息并获取焦点
  41. $('#message').val('').focus();
  42. });
  43. });
  44. });
  45.  
  46. // 为显示的消息进行Html编码
  47. function htmlEncode(value) {
  48. var encodedValue = $('<div />').text(value).html();
  49. return encodedValue;
  50. }
  51. </script>
  52. }

  9. 修改App_Start文件夹内的RoutConfig类,将Action方法默认设置为Chat.

  1. public class RouteConfig
  2. {
  3. public static void RegisterRoutes(RouteCollection routes)
  4. {
  5. routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  6. routes.MapRoute(
  7. name: "Default",
  8. url: "{controller}/{action}/{id}",
  9. defaults: new { controller = "Home", action = "Chat", id = UrlParameter.Optional }
  10. );
  11. }
  12. }

  到此,我们的例子就实现完成了,接下来我们先来看看运行效果,之后再来解释到底SignalR是如何来完成广播消息的。运行的运行结果如下。

  从运行结果,你可以发现,在任何一个窗口输入信息并发送,所有客户端将收到该消息。这样的效果在实际应用中很多,如QQ,一登录QQ的时候都会推送腾讯广告消息。

  看完了运行结果,接下来我们来分析下代码,进而来剖析下SignalR到底是如何工作的。

  按照B/S模式来看,运行程序的时候,Web页面就与SignalR的服务建立了连接,具体的建立连接的代码就是:$.connection.hub.start()。这句代码的作用就是与SignalR服务建立连接,后面的done函数表明建立连接成功后为发送按钮注册了一个click事件,当客户端输入内容点击发送按钮后,该Click事件将会触发,触发执行的操作为: chat.server.send($('#message').val())。这句代码表示调用服务端的send函数,而服务端的Send韩式又是调用所有客户端的sendMessage函数,而客户端中sendMessage函数就是将信息添加到对应的消息列表中。这样就实现了广播消息的功能了。 

  看到这里,有人是否会有疑问,前面的实现都只用到了集线器对象,而没有用到持久连接对象。其实并不是如此,$.connection这句代码就是使用持久连接对象,当然你也可以在重新OnConnected方法来查看监控客户端的连接情况,更新的代码如下所示:

  1. public class ServerHub : Hub
  2. {
  3. private static readonly char[] Constant =
  4. {
  5. '', '', '', '', '', '', '', '', '', '',
  6. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  7. 'w', 'x', 'y', 'z',
  8. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
  9. 'W', 'X', 'Y', 'Z'
  10. };
  11.  
  12. /// <summary>
  13. /// 供客户端调用的服务器端代码
  14. /// </summary>
  15. /// <param name="message"></param>
  16. public void Send(string message)
  17. {
  18. var name = GenerateRandomName();
  19.  
  20. // 调用所有客户端的sendMessage方法
  21. Clients.All.sendMessage(name, message);
  22. }
  23.  
  24. /// <summary>
  25. /// 客户端连接的时候调用
  26. /// </summary>
  27. /// <returns></returns>
  28. public override Task OnConnected()
  29. {
  30. Trace.WriteLine("客户端连接成功");
  31. return base.OnConnected();
  32. }
  33. /// <summary>
  34. /// 产生随机用户名函数
  35. /// </summary>
  36. /// <param name="length">用户名长度</param>
  37. /// <returns></returns>
  38. public static string GenerateRandomName(int length)
  39. {
  40. var newRandom = new System.Text.StringBuilder();
  41. var rd = new Random();
  42. for (var i = ; i < length; i++)
  43. {
  44. newRandom.Append(Constant[rd.Next()]);
  45. }
  46. return newRandom.ToString();
  47. }
  48. }

  这样在运行页面的时候,将在输出窗口看到“客户端连接成功”字样。运行效果如下图所示:

  在第二部分介绍的时候说道,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,为了验证这一点,可以在Chrome中F12来查看源码就明白了,具体如下图所示:

  看到上图,你也就明白了为什么Chat.cshtml页面需要引入"signalr/hubs"脚本库了吧。

  1. <!--引用SignalR库. -->
  2. <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
  3. <!--引用自动生成的SignalR 集线器(Hub)脚本.在运行的时候在浏览器的Source下可看到 -->
  4. <script src="~/signalr/hubs"></script>

四、在桌面程序中如何使用Asp.net SignalR

  上面部分介绍了SignalR在Asp.net MVC 中的实现,这部分将通过一个例子来看看SignalR在WPF或WinForm是如何使用的。其实这部分实现和Asp.net MVC中非常相似,主要不同在于,Asp.net MVC中的SignalR服务器寄宿在IIS中,而在WPF中应用,我们把SignalR寄宿在WPF客户端中。

下面让我们看看SignalR服务端的实现。

  1. /// <summary>
  2. /// 启动SignalR服务,将SignalR服务寄宿在WPF程序中
  3. /// </summary>
  4. private void StartServer()
  5. {
  6. try
  7. {
  8. SignalR = WebApp.Start(ServerUri); // 启动SignalR服务
  9. }
  10. catch (TargetInvocationException)
  11. {
  12. WriteToConsole("一个服务已经运行在:" + ServerUri);
  13. // Dispatcher回调来设置UI控件状态
  14. this.Dispatcher.Invoke(() => ButtonStart.IsEnabled = true);
  15. return;
  16. }
  17.  
  18. this.Dispatcher.Invoke(() => ButtonStop.IsEnabled = true);
  19. WriteToConsole("服务已经成功启动,地址为:" + ServerUri);
  20. }
  21.  
  22. public class ChatHub : Hub
  23. {
  24. public void Send(string name, string message)
  25. {
  26. Clients.All.addMessage(name, message);
  27. }
  28.  
  29. public override Task OnConnected()
  30. {
  31. //
  32. Application.Current.Dispatcher.Invoke(() =>
  33. ((MainWindow)Application.Current.MainWindow).WriteToConsole("客户端连接,连接ID是: " + Context.ConnectionId));
  34.  
  35. return base.OnConnected();
  36. }
  37.  
  38. public override Task OnDisconnected(bool stopCalled)
  39. {
  40. Application.Current.Dispatcher.Invoke(() =>
  41. ((MainWindow)Application.Current.MainWindow).WriteToConsole("客户端断开连接,连接ID是: " + Context.ConnectionId));
  42.  
  43. return base.OnDisconnected(true);
  44. }
  45. }
  46.  
  47. public class Startup
  48. {
  49. public void Configuration(IAppBuilder app)
  50. {
  51. // 有关如何配置应用程序的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=316888
  52. // 允许CORS跨域
  53. //app.UseCors(CorsOptions.AllowAll);
  54. app.MapSignalR();
  55. }
  56. }

  通过上面的代码,我们SignalR服务端的实现就完成了,其实现逻辑与Asp.net MVC的代码类似。

  接下来,让我们看看,WPF客户端是如何连接和与服务器进行通信的。具体客户端的实现如下:

  1. public IHubProxy HubProxy { get; set; }
  2. const string ServerUri = "http://localhost:8888/signalr";
  3. public HubConnection Connection { get; set; }
  4.  
  5. public MainWindow()
  6. {
  7. InitializeComponent();
  8.  
  9. // 窗口启动时开始连接服务
  10. ConnectAsync();
  11. }
  12.  
  13. /// <summary>
  14. /// 发送消息
  15. /// </summary>
  16. /// <param name="sender"></param>
  17. /// <param name="e"></param>
  18. private void ButtonSend_Click(object sender, RoutedEventArgs e)
  19. {
  20. // 通过代理来调用服务端的Send方法
  21. // 服务端Send方法再调用客户端的AddMessage方法将消息输出到消息框中
  22. HubProxy.Invoke("Send", GenerateRandomName(), TextBoxMessage.Text.Trim());
  23.  
  24. TextBoxMessage.Text = String.Empty;
  25. TextBoxMessage.Focus();
  26. }
  27.  
  28. private async void ConnectAsync()
  29. {
  30. Connection = new HubConnection(ServerUri);
  31. Connection.Closed += Connection_Closed;
  32.  
  33. // 创建一个集线器代理对象
  34. HubProxy = Connection.CreateHubProxy("ChatHub");
  35.  
  36. // 供服务端调用,将消息输出到消息列表框中
  37. HubProxy.On<string, string>("AddMessage", (name, message) =>
  38. this.Dispatcher.Invoke(() =>
  39. RichTextBoxConsole.AppendText(String.Format("{0}: {1}\r", name, message))
  40. ));
  41.  
  42. try
  43. {
  44. await Connection.Start();
  45. }
  46. catch (HttpRequestException)
  47. {
  48. // 连接失败
  49. return;
  50. }
  51.  
  52. // 显示聊天控件
  53. ChatPanel.Visibility = Visibility.Visible;
  54. ButtonSend.IsEnabled = true;
  55. TextBoxMessage.Focus();
  56. RichTextBoxConsole.AppendText("连上服务:" + ServerUri + "\r");
  57. }

  上面的代码也就是WPF客户端实现的核心代码,主要逻辑为,客户端启动的时候就调用Connection.Start方法与服务器进行连接。然后通过HubProxy代理类来调用集线器中Send方法,而集线器中的Send方法又通过调用客户端的addMessage方法将消息输出到客户端的消息框中进行显示,从而完成消息的推送过程。接下来,让我们看看其运行效果:

  从上面的运行效果看出,其效果和Asp.net MVC上的效果是一样的。

五、总结

  到这里,本专题的所有内容就结束了,这篇SignalR快速入门也是本人在学习SignalR过程中的一些心得体会,希望可以帮助一些刚接触SignalR的朋友快速入门。本篇主要实现了SignalR的广播消息的功能,可以实现手机端消息推送的功能,接下来一篇将介绍如何使用SignalR实现一对一的聊天。

  本文所有源码:SignalR快速入门

[Asp.net 开发系列之SignalR篇]专题一:Asp.net SignalR快速入门的更多相关文章

  1. [Asp.net 开发系列之SignalR篇]专题六:使用SignalR实现消息提醒

    一.引言 前面一篇文章我介绍了如何使用SignalR实现图片的传输,然后对于即时通讯应用来说,消息提醒是必不可少的.现在很多网站的都有新消息的提醒功能.自然对于SignalR系列也少不了这个功能的实现 ...

  2. [Asp.net 开发系列之SignalR篇]专题二:使用SignalR实现酷炫端对端聊天功能

    一.引言 在前一篇文章已经详细介绍了SignalR了,并且简单介绍它在Asp.net MVC 和WPF中的应用.在上篇博文介绍的都是群发消息的实现,然而,对于SignalR是为了实时聊天而生的,自然少 ...

  3. [Asp.net 开发系列之SignalR篇]专题四:使用SignalR实现发送图片

    一.引言 在前一篇博文已经介绍了如何使用SignalR来实现聊天室的功能,在这篇文章中,将实现如何使用SignalR来实现发送图片的功能. 二.实现发送图片的思路 我还是按照之前的方式来讲述这篇文章, ...

  4. [Asp.net 开发系列之SignalR篇]专题三:使用SignalR实现聊天室的功能

    一.引言 在前一篇文章中,我向大家介绍了如何实现实现端对端聊天的功能的,在这一篇文章中将像大家如何使用SignalR实现群聊这样的功能. 二.实现思路 要想实现群聊的功能,首先我们需要创建一个房间,然 ...

  5. 【Windows10 IoT开发系列】配置篇

    原文:[Windows10 IoT开发系列]配置篇 Windows10 For IoT是Windows 10家族的一个新星,其针对不同平台拥有不同的版本.而其最重要的一个版本是运行在Raspberry ...

  6. openlayers5-webpack 入门开发系列一初探篇(附源码下载)

    前言 openlayers5-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载 ...

  7. leaflet-webpack 入门开发系列一初探篇(附源码下载)

    前言 leaflet-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载地址 w ...

  8. [置顶]【实用 .NET Core开发系列】- 导航篇

    前言 此系列从出发点来看,是 上个系列的续篇, 上个系列因为后面工作的原因,后面几篇没有写完,后来.NET Core出来之后,注意力就转移到了.NET Core上,所以再也就没有继续下去,此是原因之一 ...

  9. openlayers4 入门开发系列之风场图篇

    前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...

随机推荐

  1. MacDev.Mach-O.Programming-Part-III:MachOView-v2.4.9200.dmg-crash

    MachOView-v2.4.9200.dmg Crash 在OS X(其版本号: 10.11.6 (15G31))下载MachOView-2.4.9200.dmg后,打开Fat Binary后,Ma ...

  2. CentOS 6.5升级Python和安装IPython

    <转自:http://www.noanylove.com/2014/10/centos-6-5-sheng-ji-python-he-an-zhuang-ipython/>自己常用.以做备 ...

  3. windows 8.1 下蓝屏报错:SYSTEM_SERVICE_EXCEPTION(NETIO.SYS)的解决办法

         大概2周前,电脑突然蓝屏了,我上网查了一下解决办法,因为大部分内容是英文的,所以我只大概看了下,看到这个问题好像是由于软件冲突造成的,于是就把小红伞去掉了,而那天电脑也真的没有再蓝屏(之前大 ...

  4. C#类和接口、虚方法和抽象方法及值类型和引用类型的区别

    1.C#类和接口的区别接口是负责功能的定义,项目中通过接口来规范类,操作类以及抽象类的概念!而类是负责功能的具体实现!在类中也有抽象类的定义,抽象类与接口的区别在于:抽象类是一个不完全的类,类里面有抽 ...

  5. Python的包管理工具Pip (zz )

    Python的包管理工具Pip 接触了Ruby,发现它有个包管理工具RubyGem很好用,并且有很完备的文档系统http://rdoc.info 发现Python下也有同样的工具,包括easy_ins ...

  6. js获取单选按钮的值

    function a(){ var v=document.getElementsByName("radio"); ;i<v.length;i++){ if(v[i].chec ...

  7. OD使用教程3

    reverseMe爆破: 跳转指令 让跳转已实现,把z的1改成0 按F8走,继续把z的1改成0,实现跳转 根据跳转指令,改变s或o,使跳转不实现 指令如上使跳转不实现 继续按f8往下走然后成功  

  8. XAMARIN +VS2015 ANDROID 开发判断gps 是否打开。

    在获取位置的时候首先要判断gps是否打开,如果没有打开就要提示打开,当然最友好的就是直接调转到打开界面. LocationManager alm = (LocationManager)this.Get ...

  9. 开启Python之路

    开始自学Python 环境配置 自己百度去!!! 计算与变量 字符创.列表.元组和字典 简单的画图 使用if和else条件控制语句 循环 使用函数和模块来重用代码 使用类和对象 Python内建函数的 ...

  10. 编译gtk+程序报错gcc: pkg-config --cflags --libs gtk+-2.0: 没有那个文件或目录

    第一次接触gtk+.在网上搜罗良一番,装好相应的库后,编写了第一hello程序.在编译时输入以下命令:gcc -o hello hello.c 'pkg-config --cflags --libs ...