浅尝SignalR

垂涎WebSocket已经很久了,终于有了学习的机会。

对于ASP.NET,试着用了下Fleck,比较方便,但是问题多多,一个挂则全挂,没时间去研究。

之后,找到了SignalR,看大神们的博客后感觉简单且好用。

原理神马的,后面再来贴,先记录下Demo小激动一下。

首先,创建一个MVC项目SignalRDemo。

接着,引入SignalR:

引用完成后,就可以看到解决方案中多了这些:

而且,在》添加》新建项,中会看到这里多了SignalR,

 接下来我们创建一个SignalR永久连接类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.SignalR;

namespace SignalRDemo.SignalR
{
    public class MySignalRConnection : PersistentConnection
    {
        protected override Task OnConnected(IRequest request, string connectionId)
        {
            return Connection.Send(connectionId, "Welcome!");
        }

        protected override Task OnReceived(IRequest request, string connectionId, string data)
        {
            return Connection.Broadcast(data);
        }
    }
}

可以看到,新建的SignalR永久性连接类继承了PersistentConnection类,并且重写了OnConnected和OnReceived方法。这两个方法一个是建立链接时触发,一个是客户端向服务器发消息时触发。

#region 程序集 Microsoft.AspNet.SignalR.Core, Version=2.4.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// ...packagesMicrosoft.AspNet.SignalR.Core.2.4.0lib
et45Microsoft.AspNet.SignalR.Core.dll
#endregion

using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Hosting;
using Microsoft.AspNet.SignalR.Infrastructure;
using Microsoft.AspNet.SignalR.Messaging;
using Microsoft.AspNet.SignalR.Tracing;
using Microsoft.AspNet.SignalR.Transports;
using Newtonsoft.Json;

namespace Microsoft.AspNet.SignalR
{
    //
    // 摘要:
    //     Represents a connection between client and server.
    public abstract class PersistentConnection
    {
        protected PersistentConnection();

        //
        // 摘要:
        //     Gets the Microsoft.AspNet.SignalR.IConnection for the Microsoft.AspNet.SignalR.PersistentConnection.
        public IConnection Connection { get; }
        //
        // 摘要:
        //     Gets the Microsoft.AspNet.SignalR.IConnectionGroupManager for the Microsoft.AspNet.SignalR.PersistentConnection.
        public IConnectionGroupManager Groups { get; }
        public IMemoryPool Pool { get; set; }
        protected IAckHandler AckHandler { get; }
        protected IPerformanceCounterManager Counters { get; }
        protected JsonSerializer JsonSerializer { get; }
        protected IMessageBus MessageBus { get; }
        protected IProtectedData ProtectedData { get; }
        protected virtual TraceSource Trace { get; }
        protected ITraceManager TraceManager { get; }
        protected ITransport Transport { get; }
        protected IUserIdProvider UserIdProvider { get; }

        public bool Authorize(IRequest request);
        public virtual void Initialize(IDependencyResolver resolver);
        //
        // 摘要:
        //     OWIN entry point.
        //
        // 参数:
        //   environment:
        public Task ProcessRequest(IDictionary<string, object> environment);
        //
        // 摘要:
        //     Handles all requests for Microsoft.AspNet.SignalR.PersistentConnections.
        //
        // 参数:
        //   context:
        //     The Microsoft.AspNet.SignalR.Hosting.HostContext for the current request.
        //
        // 返回结果:
        //     A System.Threading.Tasks.Task that completes when the Microsoft.AspNet.SignalR.PersistentConnection
        //     pipeline is complete.
        //
        // 异常:
        //   T:System.InvalidOperationException:
        //     Thrown if connection wasn't initialized. Thrown if the transport wasn't specified.
        //     Thrown if the connection id wasn't specified.
        public virtual Task ProcessRequest(HostContext context);
        //
        // 摘要:
        //     Called before every request and gives the user a authorize the user.
        //
        // 参数:
        //   request:
        //     The Microsoft.AspNet.SignalR.IRequest for the current connection.
        //
        // 返回结果:
        //     A boolean value that represents if the request is authorized.
        protected virtual bool AuthorizeRequest(IRequest request);
        //
        // 摘要:
        //     Returns the signals used in the Microsoft.AspNet.SignalR.PersistentConnection.
        //
        // 参数:
        //   userId:
        //     The user id for the current connection.
        //
        //   connectionId:
        //     The id of the incoming connection.
        //
        // 返回结果:
        //     The signals used for this Microsoft.AspNet.SignalR.PersistentConnection.
        protected virtual IList<string> GetSignals(string userId, string connectionId);
        //
        // 摘要:
        //     Called when a new connection is made.
        //
        // 参数:
        //   request:
        //     The Microsoft.AspNet.SignalR.IRequest for the current connection.
        //
        //   connectionId:
        //     The id of the connecting client.
        //
        // 返回结果:
        //     A System.Threading.Tasks.Task that completes when the connect operation is complete.
        protected virtual Task OnConnected(IRequest request, string connectionId);
        //
        // 摘要:
        //     Called when a connection disconnects gracefully or due to a timeout.
        //
        // 参数:
        //   request:
        //     The Microsoft.AspNet.SignalR.IRequest for the current connection.
        //
        //   connectionId:
        //     The id of the disconnected connection.
        //
        //   stopCalled:
        //     true, if stop was called on the client closing the connection gracefully; false,
        //     if the connection has been lost for longer than the Microsoft.AspNet.SignalR.Configuration.IConfigurationManager.DisconnectTimeout.
        //     Timeouts can occur in scaleout when clients reconnect with another server.
        //
        // 返回结果:
        //     A System.Threading.Tasks.Task that completes when the disconnect operation is
        //     complete.
        protected virtual Task OnDisconnected(IRequest request, string connectionId, bool stopCalled);
        //
        // 摘要:
        //     Called when data is received from a connection.
        //
        // 参数:
        //   request:
        //     The Microsoft.AspNet.SignalR.IRequest for the current connection.
        //
        //   connectionId:
        //     The id of the connection sending the data.
        //
        //   data:
        //     The payload sent to the connection.
        //
        // 返回结果:
        //     A System.Threading.Tasks.Task that completes when the receive operation is complete.
        protected virtual Task OnReceived(IRequest request, string connectionId, string data);
        //
        // 摘要:
        //     Called when a connection reconnects after a timeout.
        //
        // 参数:
        //   request:
        //     The Microsoft.AspNet.SignalR.IRequest for the current connection.
        //
        //   connectionId:
        //     The id of the re-connecting client.
        //
        // 返回结果:
        //     A System.Threading.Tasks.Task that completes when the re-connect operation is
        //     complete.
        protected virtual Task OnReconnected(IRequest request, string connectionId);
        //
        // 摘要:
        //     Called when a connection reconnects after a timeout to determine which groups
        //     should be rejoined.
        //
        // 参数:
        //   request:
        //     The Microsoft.AspNet.SignalR.IRequest for the current connection.
        //
        //   groups:
        //     The groups the calling connection claims to be part of.
        //
        //   connectionId:
        //     The id of the reconnecting client.
        //
        // 返回结果:
        //     A collection of group names that should be joined on reconnect
        protected virtual IList<string> OnRejoiningGroups(IRequest request, IList<string> groups, string connectionId);
        //
        // 摘要:
        //     Validates the connection token and extracts the connection ID from it.
        //
        // 参数:
        //   context:
        //     The Microsoft.AspNet.SignalR.Hosting.HostContext representing the request.
        //
        //   connectionToken:
        //     The connection token to validate.
        //
        //   connectionId:
        //     If this method returns true, this output parameter receives the connection ID
        //     contained within the token.
        //
        //   message:
        //     If this method returns false, this output parameter receives an error message
        //     to report.
        //
        //   statusCode:
        //     If this method returns false, this output parameter receives an HTTP status code
        //     to report.
        //
        // 返回结果:
        //     A boolean indicating if the connection token was valid.
        protected internal virtual bool TryGetConnectionId(HostContext context, string connectionToken, out string connectionId, out string message, out int statusCode);
    }
}

先不用MQ,就写个简单的List来存储链接吧

namespace SignalRDemo.App_Start
{
    public class AppHelper
    {
        private AppHelper()
        {
            ConnectionList = new List<string>();
        }
        public static List<string> ConnectionList { get; set; }
    }
}

修改MySignalRConnection类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNet.SignalR;
using SignalRDemo.App_Start;

namespace SignalRDemo.SignalR
{
    public class MySignalRConnection : PersistentConnection
    {
        protected override Task OnConnected(IRequest request, string connectionId)
        {
            if (!AppHelper.ConnectionList.Contains(connectionId))
            {
                AppHelper.ConnectionList.Add(connectionId);
            }
            Connection.Send(AppHelper.ConnectionList, "New In!");
            return Connection.Send(connectionId, "Welcome!");
        }

        protected override Task OnReceived(IRequest request, string connectionId, string data)
        {
            return Connection.Broadcast(data);
        }

        protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled)
        {
            if (AppHelper.ConnectionList.Contains(connectionId))
            {
                var oldList = new List<string>();
                oldList.AddRange(AppHelper.ConnectionList);
                AppHelper.ConnectionList.Remove(connectionId);
                return Connection.Send(oldList, "out_" + connectionId);
            }
            return null;
        }
    }
}

 在Startup.cs中添加

namespace SignalRDemo
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 有关如何配置应用程序的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=316888

            app.MapSignalR<SignalR.MySignalRConnection>("/mc");
        }
    }
}

 测试页面

@{
    ViewBag.Title = "SignalR Demo";
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/signalR");
}
<style type="text/css">
    .row{
        margin:20px;
    }

    .msgbox{
        margin:10px 0;
        height:400px;
        overflow-y:auto;
    }
</style>

<div class="row">
    <input id="ipt_text" type="text" class="" />
    <input id="submit" type="button" value="发送" />
    <div id="msgshow" class="msgbox">
</div> </div> <script type="text/javascript"> $(function () { var $ipt_text = $('#ipt_text'), $submit = $('#submit'), $msgshow = $('#msgshow'); var conn = $.connection("/mc"); conn.start().done(function (data) { conn.send('大家好,我是新来的,请多多关照'); }).fail(function (msg) { alert(msg); }); function log(msg) { $msgshow.append('<p>【' + new Date().Format("yyyy-MM-dd hh:mm:ss") + '' + msg + '</p>'); } conn.received(function (msg) { log(msg); }); $submit.on('click', function (e) { var txt = $ipt_text.val(); if (!txt || !txt.length) return; conn.send(txt); }); }); </script>

至此,整个Demo就写完了,下面是测试页面

需要注意的是,新建MVC项目的时候如果不是选的MVC模板,很多东西需要自己手动添加,比如 Startup.cs , BundleConfig等等。

还是记录下添加过程吧

Startup.cs

BundleConfig类需要引用System.Web.Optimization.dll,这个需要在Nuget中下载

搜索Optimization,然后安装

还需注意的是,如果要使用 @Styles.Render 和@Scripts.Render,还需要在View/Web.config中配置System.Web.Optimization.dll域名

后续要做的事情还很多哦

连接认证

权限控制

消息队列

消息过滤

等等

原文地址:https://www.cnblogs.com/qiaoge0923/p/10251525.html