• SignalR 简易聊天室+SignalR如何跨域


    十年河东,十年河西,莫欺少年穷

    学无止境,精益求精

    首先分享下源码:https://download.csdn.net/download/wolongbb/12768075

    一个在线聊天室,比本篇博客更齐全

    截图如下:

    环境:VS2012或以上,Frm版本4.5或以上,我用的是4.5.2

    1、在 Visual Studio 中,创建一个 ASP.NET MVC Web 应用程序。

    2、右键项目,添加hub类,并命名为:ChatHub【添加后,项目会自动引用SignalR的相关DLL程序集及会在Scripts文件中生成SignalR客户端JS文件】

    JS如下:

     ChatHub 类的代码如下

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Microsoft.AspNet.SignalR;
    using System.Threading.Tasks;
    
    namespace SignalRTx.ChatHubServer
    {
        public class ChatHub : Hub
        {
            public static List<string> Connectedlst = new List<string>();
            /// <summary>
            /// 当客户端连接时
            /// </summary>
            /// <returns></returns>
            public override Task OnConnected()
            {
                var ConnectionId = Context.ConnectionId;
                Connectedlst.Add(ConnectionId);
                return base.OnConnected();
            }
            /// <summary>
            ///  当客户端断开连接时,例如用户关闭页面,刷新页面
            /// </summary>
            /// <param name="stopCalled"></param>
            /// <returns></returns>
            public override Task OnDisconnected(bool stopCalled)
            {
                var ConnectionId = Context.ConnectionId;
                Connectedlst.Remove(ConnectionId);
                return base.OnDisconnected(stopCalled);
            }
            /// <summary>
            /// 当客户端重连时
            /// </summary>
            /// <returns></returns>
            public override Task OnReconnected()
            {
                var ConnectionId = Context.ConnectionId;
                if (!Connectedlst.Contains(ConnectionId))
                {
                    Connectedlst.Add(ConnectionId);
                }
                return base.OnReconnected();
            }
    
            /// <summary>
            /// 这是一组广播消息 所有客户端均可收到此消息
            /// broadcastMessage 是客户端的一个JS方法,也是回调函数,用于更新客户端
            /// send 则是服务端的方法,用于客户端调用
            /// </summary>
            /// <param name="name"></param>
            /// <param name="message"></param>
            public void Send(string name, string message)
            {
                Clients.All.broadcastMessage(name, message);
            }
        }
    }
    View Code

    在项目中添加命名为Startup的类,如下:

    using System;
    using Microsoft.AspNet.SignalR;
    using Microsoft.Owin;
    using Microsoft.Owin.Cors;
    using Owin;
    
    [assembly: OwinStartup(typeof(SignalRTx.ChatHubServer.Startup))]
    namespace SignalRTx.ChatHubServer
    {
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // Any connection or hub wire up and configuration should go here
                app.MapSignalR();
            }
        }
    
    
    }
    View Code

    在解决方案资源管理器中,右键单击项目,然后选择 "添加 > " HTML 页",如下:

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR Simple Chat</title>
        <style type="text/css">
            .container {
                background-color: #99CCFF;
                border: thick solid #808080;
                padding: 20px;
                margin: 20px;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <input type="text" id="message" />
            <input type="button" id="sendmessage" value="Send" />
            <input type="hidden" id="displayname" />
            <ul id="discussion">
            </ul>
        </div>
        <!--Script references. -->
        <!--Reference the jQuery library. -->
        <script src="Scripts/jquery-3.4.1.js"></script>
        <!--Reference the SignalR library. -->
        <script src="Scripts/jquery.signalR-2.2.2.js"></script>
        <!--Reference the autogenerated SignalR hub script. -->
        <script src="signalr/hubs"></script>
        <!--Add script to update the page and send messages.-->
        <script type="text/javascript">
            $(function () {
                // Declare a proxy to reference the hub.
                var chat = $.connection.chatHub;
                // Create a function that the hub can call to broadcast messages.
                chat.client.broadcastMessage = function (name, message) {
                    // Html encode display name and message.
                    var encodedName = $('<div />').text(name).html();
                    var encodedMsg = $('<div />').text(message).html();
                    // Add the message to the page.
                    $('#discussion').append('<li><strong>' + encodedName
                        + '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
                };
                // Get the user name and store it to prepend to messages.
                $('#displayname').val(prompt('Enter your name:', ''));
                // Set initial focus to message input box.
                $('#message').focus();
                // Start the connection.
                $.connection.hub.start().done(function () {
                    $('#sendmessage').click(function () {
                        // Call the Send method on the hub.【服务端方法:send】
                        chat.server.send($('#displayname').val(), $('#message').val());
                        // Clear text box and reset focus for next comment.
                        $('#message').val('').focus();
                    });
                });
            });
        </script>
    </body>
    </html>
    View Code

    运行这个HTML页面,效果如下:

     聊天内容虽说不那么好吧,但我们对SignalR如何实现跨域还是非常认真的,那么怎么操作才可以跨域通信呢?

    要实现SigNalR跨域,首选我们需要安装一个包,执行如下命令:

    Install-Package Microsoft.Owin.Cors

    然后修改我们的Startup类,如下:

    using System;
    using Microsoft.AspNet.SignalR;
    using Microsoft.Owin;
    using Microsoft.Owin.Cors;
    using Owin;
    
    [assembly: OwinStartup(typeof(SignalRTx.ChatHubServer.Startup))]
    namespace SignalRTx.ChatHubServer
    {
        //public class Startup
        //{
        //    public void Configuration(IAppBuilder app)
        //    {
        //        // Any connection or hub wire up and configuration should go here
        //        app.MapSignalR();
        //    }
        //}
    
        public class Startup
        {
            public void Configuration(IAppBuilder app)
            {
                // Branch the pipeline here for requests that start with "/signalr"
                app.Map("/signalr", map =>
                {
                    // Setup the CORS middleware to run before SignalR.
                    // By default this will allow all origins. You can 
                    // configure the set of origins and/or http verbs by
                    // providing a cors options with a different policy.
                    map.UseCors(CorsOptions.AllowAll);
                    var hubConfiguration = new HubConfiguration
                    {
                         //You can enable JSONP by uncommenting line below.
                         //JSONP requests are insecure but some older browsers (and some
                         //versions of IE) require JSONP to work cross domain
                         EnableJSONP = true
                    };
                    // Run the SignalR pipeline. We're not using MapSignalR
                    // since this branch already runs under the "/signalr"
                    // path.
                    map.RunSignalR(hubConfiguration);
                });
            }
        }
    }
    View Code

    首先把你的项目复制一个副本出来,放到其他文件夹中,然后修改你复制项目的运行端口号,如下:

    最后,客户端动态引用指定URL及端口号的signalR,修改副本HTML如下:

    <!DOCTYPE html>
    <html>
    <head>
        <title>SignalR Simple Chat</title>
        <style type="text/css">
            .container {
                background-color: #99CCFF;
                border: thick solid #808080;
                padding: 20px;
                margin: 20px;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <input type="text" id="message" />
            <input type="button" id="sendmessage" value="Send" />
            <input type="hidden" id="displayname" />
            <ul id="discussion">
            </ul>
        </div>
        <!--Script references. -->
        <!--Reference the jQuery library. -->
        <script src="Scripts/jquery-3.4.1.js"></script>
        <!--Reference the SignalR library. -->
        <script src="Scripts/jquery.signalR-2.2.2.js"></script>
        <!--Reference the autogenerated SignalR hub script. -->
        <script src="https://localhost:44330/signalr/hubs"></script>
        <!--Add script to update the page and send messages.-->
        <script type="text/javascript">
            $(function () {
                // Declare a proxy to reference the hub.
                var chat = $.connection.chatHub;
                chat.connection.url = "https://localhost:44330/signalr";
                // Create a function that the hub can call to broadcast messages.
                chat.client.broadcastMessage = function (name, message) {
                    // Html encode display name and message.
                    var encodedName = $('<div />').text(name).html();
                    var encodedMsg = $('<div />').text(message).html();
                    // Add the message to the page.
                    $('#discussion').append('<li><strong>' + encodedName
                        + '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
                };
                // Get the user name and store it to prepend to messages.
                $('#displayname').val(prompt('Enter your name:', ''));
                // Set initial focus to message input box.
                $('#message').focus();
                // Start the connection.
                $.connection.hub.start().done(function () {
                    $('#sendmessage').click(function () {
                        // Call the Send method on the hub.【服务端方法:send】
                        chat.server.send($('#displayname').val(), $('#message').val());
                        // Clear text box and reset focus for next comment.
                        $('#message').val('').focus();
                    });
                });
            });
        </script>
    </body>
    </html>
    View Code

    变更点:

     这样就可以跨域了,下面我们来模拟下跨域的请求,如下:

     1、启动并调试服务端项目,端口为:44330,并把chathub所有方法都打上断点,

    /2、启动不调试你的副本项目,端口为:44333,我们通过观察断点是否能进入判断跨域请求是否成功

     证明跨域访问成功。

    聊天内容如下:

    最后记录下如何定向发送指定的连接

            static readonly IHubContext _myHubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
            public ActionResult SendMsg()
            {
                _myHubContext.Clients.Client(ChatHub.UserLst[0].ConnectionId).broadcastMessage("服务器", "服务器");
                return View();
            }

    客户端有个 broadcastMessage 方法,接收两个参数,如下:

                chat.client.broadcastMessage = function (msg1, msg2) {
                    console.log(msg1 + "" + msg2);
                };

    最后展示下我的成果【含跨域】:

    Hub

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Web;
    using Microsoft.AspNet.SignalR;
    using Microsoft.AspNet.SignalR.Hubs;
    using Newtonsoft.Json.Linq;
    using Newtonsoft.Json;
    using Iot.Common;
    using Iot.Model;
    using Iot.Dal.Device;
    using Iot.Factory;
    using Iot.Model.Pagination;
    
    namespace Iot.WebSite.IotHubs
    {
        /// <summary>
        /// 集线器
        /// </summary>
        [HubName("ChatRoomHub")]
        public class ChatHub : Hub
        {
           static string Key = CommEnum.DeviceTypeEnm.YK.ToString();
            /// <summary>
            /// IHubContext
            /// </summary>
            static readonly IHubContext _myHubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
            /// <summary>
            /// 在线信息
            /// </summary>
            public static List<SigRConnectionModel> UserLst = new List<SigRConnectionModel>();
            /// <summary>
            /// 当客户端连接时
            /// </summary>
            /// <returns></returns>
            public override Task OnConnected()
            {
                return base.OnConnected();
            }
            /// <summary>
            /// 这是一组广播消息 所有客户端均可收到此消息
            /// broadcastMessage 是客户端的一个JS方法,也是回调函数,用于更新客户端
            /// send 则是服务端的方法,用于客户端调用
            /// </summary>
            /// <param name="name"></param>
            /// <param name="message"></param>
            public void SendBroadcastMsg(string name, string message)
            {
                Clients.All.SendBroadcastMsg(name, message);
            }
            /// <summary>
            /// 用户进入时
            /// </summary>
            /// <param name="OpenId"></param>
            /// <param name="devinceNum"></param>
            public void userEnter(string OpenId, string devinceNum)
            {
                if(!string.IsNullOrEmpty(OpenId) && !string.IsNullOrEmpty(devinceNum))
                {
                    var ConnectionId = Context.ConnectionId;
                    SigRConnectionModel u = new SigRConnectionModel()
                    {
                        ConnectionId = ConnectionId,
                        OpenId = OpenId,
                        devinceNum = devinceNum
                    };
                    UserLst.Add(u);
                    //注意:所有的连接信息 均放在云酷的MongoDb中
                    var M = SignalrFactory.GetTarget(Key);
                    M.AddSigRConntion(u);
                    _myHubContext.Clients.Client(ConnectionId).OnConnected(JsonConvert.SerializeObject(CommonBaseResponse.SetResponse<SigRConnectionModel>(u, true, "连接成功", 0)));
                }
            }
    
            /// <summary>
            ///  当客户端断开连接时,例如用户关闭页面,刷新页面
            /// </summary>
            /// <param name="stopCalled"></param>
            /// <returns></returns>
            public override Task OnDisconnected(bool stopCalled)
            {
                var ConnectionId = Context.ConnectionId;
                var ef = UserLst.Where(A => A.ConnectionId == ConnectionId).FirstOrDefault();
                if (ef != null)
                {
                    UserLst.Remove(ef);
    
                    SigRConnectionModel u = new SigRConnectionModel()
                    {
                        ConnectionId = ConnectionId,
                        OpenId = ef.OpenId,
                        devinceNum = ef.devinceNum
                    };
                    _myHubContext.Clients.Client(ConnectionId).OnDisconnected(JsonConvert.SerializeObject(CommonBaseResponse.SetResponse<SigRConnectionModel>(u, true, "断开连接", 1)));
                }
                return base.OnDisconnected(stopCalled);
            }
            /// <summary>
            /// 当客户端重连时,需要从mongoDb读取连接的信息
            /// </summary>
            /// <returns></returns>
            public override Task OnReconnected()
            {
                var ConnectionId = Context.ConnectionId;
                var M = SignalrFactory.GetTarget(Key);
                BasePaginationModel Pagination = new BasePaginationModel()
                {
                    PageNumber = 1,
                    PageSize = 1
                };
                var ef = M.GetSigRConntion(ConnectionId, ref Pagination);
                if (ef != null)
                {
                    var Connectionef = UserLst.Where(A => A.ConnectionId == ConnectionId).FirstOrDefault();
                    SigRConnectionModel u = new SigRConnectionModel()
                    {
                        ConnectionId = ConnectionId,
                        OpenId = Connectionef.OpenId,
                        devinceNum = Connectionef.devinceNum
                    };
                    if (Connectionef == null)
                    {
                        UserLst.Add(u);
                    }
                    _myHubContext.Clients.Client(ConnectionId).OnReconnected(JsonConvert.SerializeObject(CommonBaseResponse.SetResponse<SigRConnectionModel>(u, true, "重连成功", 2)));
                }
                return base.OnReconnected();
            }
    
        }
    
    }
    View Code

    Startup

    using Microsoft.AspNet.SignalR;
    using Microsoft.Owin;
    using Microsoft.Owin.Cors;
    using Owin;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    
    [assembly: OwinStartup(typeof(Iot.WebSite.IotHubs.Startup))]
    namespace Iot.WebSite.IotHubs
    {
        /// <summary>
        /// 支持跨域
        /// </summary>
        public class Startup
        {
            /// <summary>
            /// 注册集线器-signalr
            /// </summary>
            /// <param name="app"></param>
            public void Configuration(IAppBuilder app)
            {
                app.Map("/signalr", map =>
                {
                    map.UseCors(CorsOptions.AllowAll);
                    var hubConfiguration = new HubConfiguration
                    {
                        EnableJSONP = true
                    };
                    map.RunSignalR(hubConfiguration);
                });
            }
        }
    
        //public class Startup
        //{
        //    /// <summary>
        //    /// 不支持跨域
        //    /// </summary>
        //    /// <param name="app"></param>
        //    public void Configuration(IAppBuilder app)
        //    {
        //        // Any connection or hub wire up and configuration should go here
        //        app.MapSignalR();
        //    }
        //}
    }
    View Code

    HTML

    @{
        Layout = null;
    }
    
    <!DOCTYPE html>
    
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Index</title>
        <script src="~/Scripts/jquery-3.4.1.js"></script>
        <script src="~/Scripts/jquery.signalR-2.2.2.js"></script>
        <script src="http://api.one5a.com/signalr/hubs"></script>
        <script type="text/javascript">
            $(function () {
                //是否处于连接状态
                var isConnected = false;
                // Declare a proxy to reference the hub.
                var chat = $.connection.ChatRoomHub;
                chat.connection.url = "http://api.one5a.com/signalr";
                //连接成功时 触发-仅限于当前连接人收到
                chat.client.OnConnected = function (data) {
                    isConnected = true;
                    console.log(data);
                };
                //连接断开时 触发-仅限于当前连接人收到
                chat.client.OnDisconnected = function (data) {
                    isConnected = false;
                    console.log(data);
                };
                //重新连接时 触发-仅限于当前连接人收到
                chat.client.OnReconnected = function (data) {
                    isConnected = true;
                    console.log(data);
                };
                //广播消息 所有客户端均可以收到
                chat.client.SendBroadcastMsg = function (name, message) {
                    console.log(name);
                    console.log(message);
                };
    
                //开门时响应
                chat.client.SigROpenDoor = function (message) {
                    console.log(message);
                };
    
                //关门时响应
                chat.client.SigRCloseDoor = function (message) {
                    console.log(message);
                };
    
                // Start the connection.
                $.connection.hub.start().done(function () {
                    chat.server.userEnter("@ViewBag.OpenId","@ViewBag.devinceNum");
                });
            });
        </script>
    </head>
    <body>
        <div> 
    
        </div>
    </body>
    </html>
    View Code

    响应效果

    重连时,根据业务需求,需要mongoDB支持。

    参考文献:

    https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/getting-started/introduction-to-signalr

    https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client

    https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr

    https://blog.csdn.net/tte_w/article/details/80881060

  • 相关阅读:
    《领域驱动设计的原则与实践》读书笔记(一)
    asp.net identity UserSecurityStamp 的作用
    Python Django Apache配置
    SQL语句优化(转载)
    【转】性能测试,影响 TPS 的一些因素
    【转】接口测试总结
    【转】两款 Web 前端性能测试工具
    【转】工作中使用Trepn Power Profiler的应用总结
    【转】nGrinder 简易使用教程
    Http协议状态码总结
  • 原文地址:https://www.cnblogs.com/chenwolong/p/13608986.html
Copyright © 2020-2023  润新知