浅尝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 // ...\packages\Microsoft.AspNet.SignalR.Core.2.4.0\lib\net45\Microsoft.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("/mc"); } } }
测试页面
@{ ViewBag.Title = "SignalR Demo"; @Scripts.Render("~/bundles/jquery") @Scripts.Render("~/bundles/signalR"); } <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('【
' + new Date().Format("yyyy-MM-dd hh:mm:ss") + '】' + msg + ''); } 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域名
后续要做的事情还很多哦
连接认证
权限控制
消息队列
消息过滤
等等