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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM