zoukankan      html  css  js  c++  java
  • SignalR 实现web浏览器客户端与服务端的推送功能

    SignalR 是一个集成的客户端与服务器库,基于浏览器的客户端和基于 ASP.NET 的服务器组件可以借助它来进行双向多步对话。 换句话说,该对话可不受限制地进行单个无状态请求/响应数据交换;它将继续,直到明确关闭。 对话通过永久连接进行,允许客户端向服务器发送多个消息,并允许服务器做出相应答复,值得注意的是,还允许服务器向客户端发送异步消息。它和AJax类似,都是基于现有的技术。本身是一个复合体。一般情况下,SignalR会使用Javascript的长轮询( long polling),实现客户端和服务端通信。在WebSockets出现以后,SignalR也支持WebSockets通信。当然SignalR也使用了服务端的任务并行处理技术以提高服务器的扩展性。它的目标整个 .NET Framework 平台,它也不限 Hosting 的应用程序,而且还是跨平台的开源项目,支持Mono 2.10+,觉得它变成是 Web API 的另一种实作选择,但是它在服务端处理联机的功能上比 ASP.NET MVC 的 Web API 要强多了,更重要的是,它可以在 Web Form 上使用。

    SignalR 内的客户端库 (.NET/JavaScript) 提供了自动管理的能力,开发人员只需要直接使用 SignalR 的 Client Library 即可,同时它的 JavaScript 库可和 jQuery 完美整合,因此能直接与像 jQuery 或 Knockout.js 一起使用。

    SignalR内部有两类对象:

    · Persistent Connection:持久性连接,用来解决长时间连接的能力,而且还可以由客户端主动向服务器要求数据,而服务器端也不需要实现太多细节,只需要处理 PersistentConnection 内所提供的五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。

    · Hub:信息交换器,用来解决 realtime 信息交换的功能,服务器端可以利用 URL 来注册一个或多个 Hub,只要连接到这个 Hub,就能与所有的客户端共享发送到服务器上的信息,同时服务器端可以调用客户端的脚本,不过它背后还是不离 HTTP 的标准,所以它看起来神奇,但它并没有那么神奇,只是 JavaScript 更强,强到可以用像 eval() 或是动态解释执行的方式,允许 JavaScript 能够动态的加载与执行方法调用而己。

    SignalR 将整个交换信息的行为封装得非常漂亮,客户端和服务器全部都使用 JSON 来沟通,在服务器端声明的所有 hub 的信息,都会一般生成 JavaScript 输出到客户端,.NET 则是依赖 Proxy 来生成代理对象,这点就和 WCF/.NET Remoting 十分类似,而 Proxy 的内部则是将 JSON 转换成对象,以让客户端可以看到对象。

    下面我们来针对Persistent Connection和Hub 做个Demo试试:

    新建一个ASP.NET MVC项目MvcApplicationSignalR,通过Nuget添加SignalR的包。

    新建一个类MyConnection 继承自 PersistentConnection ,引用SignalR命名空间,重写OnReceivedAsync 的方法,并要求 SignalR 对传入的信息做广播

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Text; 
    using SignalR; 
    using System.Threading.Tasks;

    namespace MvcApplicationSignalR 

       public  class MyConnection : PersistentConnection

        { 
           protected override Task OnReceivedAsync(IRequest request, string connectionId, string data) 
            { 
                // Broadcast data to all clients 
                data = string.Format("数据是:{0} 时间是:{1}", data, DateTime.Now.ToString()); 
                return Connection.Send(connectionId, data); 
            }

        } 
    }

    接着在 Global.asax 中加入对应路由信息,这会由 SignalR 的路由表来处理 Metadata 的输出工作,红色部分代码:

    protected void Application_Start() 

        RouteTable.Routes.MapConnection<MyConnection>("echo", "echo/{*operation}");

    这样服务器端就完成了。现在我们在项目中Master、View (Home/Index),然后加入必要的代码:

    <head> 
        <meta charset="utf-8" /> 
        <title>@ViewBag.Title</title> 
        <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" /> 
        <script src="@Url.Content("~/Scripts/jquery-1.6.4.min.js")" type="text/javascript"></script> 
        <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script> 
        <script src="@Url.Content("~/Scripts/jquery.signalR-0.5.2.min.js")" type="text/javascript"></script> 
    </head>

    @{ 
        ViewBag.Title = "Home Page"; 

    <script type="text/javascript"> 
        $(function () { 
            var connection = $.connection('echo');

            connection.received(function (data) { 
                $('#messages').append('<li>' + data + '</li>'); 
            });

            connection.start();

            $("#broadcast").click(function () { 
                connection.send($('#msg').val()); 
            }); 
            $("#btnStop").click(function () { 
                connection.stop(); 
            }); 
        });

    </script> 
    <h2>@ViewBag.Message</h2> 
    <input type="text" id="msg" /> 
    <input type="button" id="broadcast" value="发送" /> 
    <input type="button" id="btnStop" value="停止" /> 
    <ul id="messages"> 
    </ul>

    运行起来就是这个效果:

    image

    下面我们来展示 SignalR 的另一个功能:由服务器端调用客户端的 JavaScript 脚本的功能,而这个功能的要求必须是要实现成 Hub 的模式,因此我们可以顺便看到如何实现一个 Hub 类型的 SignalR 应用程序。

    向项目中加入一个类Chat继承自 Hub 类 (这是 Hub 应用程序的要求) :

    using System; 
    using System.Collections.Generic; 
    using System.Linq; 
    using System.Web; 
    using SignalR.Hubs; 
    using System.Threading.Tasks; 
    using System.Threading;

    namespace MvcApplicationSignalR 

       [HubName("geffChat")] 
       public class Chat : Hub  
       { 
           public void SendMessage(string message) 
           { 
               Clients.sendMessage(message); 
           } 
       } 
    }

    这段程序代码的用意是,在连接进到 Hub 时,将连接代码加到联机用户的集合中,等会就会使用到,因为我们会依照客户端的 ID 来调用客户端脚本。

    1. HubName:这个 atttibute 代表 client 端要如何建立对应 server 端对象的 proxy object。通过 HubName , server 端的 class name才不会被 client 绑死。如果没有设定,则会以 server 端 class name 为 HubName 默认值。

    2. 继承 Hub:继承 Hub 之后,很多对应的设计就都不用写了,我们只需要把注意力放在 client 如何送 request 给 server的 hub , server 如何通知 client 即可。

    3. public void SendMessage(string message) ,就像 WebService Method 或 PageMethod 一般, client 端通过 proxy object ,可以直接调用 server 端这个方法。后续会介绍到如何在页面上使用。

    4. Clients 属性:代表所有有使用 Chat 的页面。而 Clients 的型别是 dynamic ,因为要直接对应到 JavaScript 的对象。

    5. Clients.sendMessage(message):代表 server 端调用 Clients 上的 sendMessage 方法,也就是 JavaScript 的方法。

    6. 总结: Chat 对象职责就是当 client 端调用SendMessage() 方法后,要把这个 message ,送给所有 client 页面上呈现。以达到聊天室的功能。

    服务端的做完了,开始制作客户端,同样在Home/Index页面上增加以下html代码

    <%--很重要的一个参考,一定要加,且在这一行之前,一定要先参考jQuery.js与signalR.js--%> 
    <script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script>

    @{ 
        ViewBag.Title = "Home Page"; 

    <script type="text/javascript"> 
        $(function () { 
            var connection = $.connection('/echo');    

            connection.received(function (data) { 
                $('#messages').append('<li>' + data + '</li>'); 
            });

            connection.start();

            $("#broadcast").click(function () { 
                connection.send($('#msg').val()); 
            }); 
            $("#btnStop").click(function () { 
                connection.stop(); 
            });

            // 建立对应server端Hub class的对象,请注意geffChat的第一个字母要改成小写 
            var chat = $.connection.geffChat;

            // 定义client端的javascript function,供server端hub,通过dynamic的方式,调用所有Clients的javascript function 
            chat.sendMessage = function (message) { 
                //当server端调用sendMessage时,将server push的message数据,呈现在wholeMessage中 
                $('#wholeMessages').append('<li>' + message + '</li>'); 
            };

            $("#send").click(function () { 
                //调用叫server端的Hub对象,将#message数据传给server 
                chat.sendMessage($('#message').val()); 
                $('#message').val(""); 
            });

            //把connection打开 
            $.connection.hub.start();

        });

    </script> 
    <h2>@ViewBag.Message</h2> 
    <input type="text" id="msg" /> 
    <input type="button" id="broadcast" value="发送" /> 
    <input type="button" id="btnStop" value="停止" /> 
    <ul id="messages"> 
    </ul>

    <div> 
        <input type="text" id="message" /> 
        <input type="button" id="send" value="发送" /> 
        <div> 
            聊天室内容: 
            <br /> 
            <ul id="wholeMessages"> 
            </ul> 
        </div> 
    </div>

    1. 先引用 jQuery 与 signalR 的 js 文件。

    2. 很重要的一个步骤:加入一个 js 引用,其路径为「根目录/signalr/hubs」。 SignalR 会建立相关的 JavaScript,放置于此。

    3. 通过 $.connection.『server 端的 HubName』,即可建立对应该 hub 的 proxy object。要注意,首字母需小写。

    4. 定义 client 端上,供 server 端通知的 JavaScript function,这边的例子是 sendMessage。

    5. 当按下发送按钮时,调用 server 端的 SendMessage() 方法,只需要直接通过 proxy object 即可。要注意,首字母需小写。

    6. 记得透过 $.connection.hub.start() ,把 connection 打开。

    image

    注意:SingalR 会自动生成一个siganlr/hub 的桥接js..,在本机使用localhost测试都不会有问题。当部署到IIS的时候会发生404错误,是由于被IIS误判可能是虚拟目录…,解决方法是在web.config加入一段:

    <!-- 加入下面这一段-->

    <system.webServer>

    <validation validateIntegratedModeConfiguration="false" />

    <modules runAllManagedModulesForAllRequests="true">

    </modules>

    </system.webServer>

    参考:

    实例代码:MvcApplicationSignalR

  • 相关阅读:
    poj 2488 DFS
    畅通工程 并查集模版
    KMP 模板
    poj 1426 DFS
    poj 2528 线段数
    poj 3468 线段数 修改区间(点)
    CVPR2012文章阅读(2)A Unified Approach to Salient Object Detection via Low Rank Matrix Recovery
    如何制定目标
    Saliency Map 最新综述
    计算机视觉模式识别重要会议杂志
  • 原文地址:https://www.cnblogs.com/soundcode/p/4191013.html
Copyright © 2011-2022 走看看