.NET4.x下使用SignalR做登录、推送

.NET Core平台下记录过一篇文章了:https://www.cnblogs.com/shousiji/p/12737925.html

已经不想在.net framework下折腾了,奈何老项目也需要SignalR,故写下这篇文章。

官方示例:MVC方式前后端分离方式-后端前后端分离方式-前端

前后端方式复杂些,本文就是记录总结这个方式。项目结构如图:

一、创建服务端

1. 在vs中创建一个空的“web应用程序”,命令为“SignalRServer”

2. NuGet搜索并安装“Microsoft.AspNet.SignalR”

 3. 会自动下载Scripts文件夹,待会把它剪切到“SignalRClient”项目中

 4. NuGet搜索并安装“Microsoft.Owin.Cors”跨域组件

 5. 新建OwinStartup类,命名为“Startup.cs”

 

代码如下:

using Microsoft.AspNet.SignalR;
using Microsoft.Owin;
using Microsoft.Owin.Cors;
using Owin;

[assembly: OwinStartup(typeof(SignalRServer.Startup))]

namespace SignalRServer
{
    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);
            });
        }
    }
}

 6. 新建类,命名为“DemoChatHub.cs”,代码如下:

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace SignalRServer
{
    [HubName("demoChatHub")]
    public class DemoChatHub : Hub
    {
        /// <summary>
        /// 已登录的用户信息
        /// </summary>
        public static List<UserModel> OnlineUser { get; set; } = new List<UserModel>();

        /// <summary>
        /// 模拟存放在数据库里的用户信息
        /// </summary>
        private static readonly List<UserModel> _dbuser = new List<UserModel> {
          new UserModel{
            UserName = "test1", Password = "111", GroupName = "Group1"
          },
          new UserModel{
            UserName = "test2", Password = "111", GroupName = "Group2"
          },
          new UserModel{
            UserName = "test3", Password = "111", GroupName = "Group2"
          },
          new UserModel{
            UserName = "test4", Password = "111", GroupName = "Group1"
          },
          new UserModel{
            UserName = "test5", Password = "111", GroupName = "Group3"
          },
        };

        /// <summary>
        /// 登录验证
        /// </summary>
        [HubMethodName("login")]
        public async Task Login(string username, string password)
        {
            string connid = Context.ConnectionId;
            ResultModel result = new ResultModel
            {
                Status = 0,
                Message = "登录成功!"
            };
            if (!OnlineUser.Exists(u => u.ID == connid))
            {
                var model = _dbuser.Find(u => u.UserName == username && u.Password == password);
                if (model != null)
                {
                    model.ID = connid;
                    OnlineUser.Add(model);
                    //给当前的连接分组
                    await Groups.Add(connid, model.GroupName);
                }
                else
                {
                    result.Status = 1;
                    result.Message = "账号或密码错误!";
                }
            }
            //给当前连接返回消息
            await Clients.Client(connid).LoginResponse(result);
        }

        /// <summary>
        /// 获取所在组的在线用户
        /// </summary>
        [HubMethodName("getUsers")]
        public async Task GetUsers()
        {
            var model = OnlineUser.Find(u => u.ID == Context.ConnectionId);
            ResultModel result = new ResultModel();
            if (model == null)
            {
                result.Status = 1;
                result.Message = "请先登录!";
            }
            else
            {
                result.Status = 0;
                result.OnlineUser = OnlineUser.FindAll(u => u.GroupName == model.GroupName);
            }
            //给所在组返回消息
            await Clients.Group(model.GroupName).GetUsersResponse(result);
        }

        /// <summary>
        /// 推送消息
        /// </summary>
        [HubMethodName("sendMessage")]
        public async Task SendMessage(string user, string message)
        {
            ResultModel result = new ResultModel();
            var model = OnlineUser.Find(u => u.ID == Context.ConnectionId);
            if (model == null)
            {
                result.Status = 1;
                result.Message = "请先登录!";
            }
            else
            {
                result.Status = 0;
                result.Message = $"“{user}”发送的消息:{message}";
            }
            await Clients.Group(model.GroupName).SendMessageResponse(result);
        }

        /// <summary>
        /// 当连接成功时的处理
        /// </summary>
        public override Task OnConnected()
        {
            return base.OnConnected();
        }

        /// <summary>
        /// 当连接断开时的处理
        /// </summary>
        public override Task OnDisconnected(bool stopCalled)
        {
            string connid = Context.ConnectionId;
            var model = OnlineUser.Find(u => u.ID == connid);
            int count = OnlineUser.RemoveAll(u => u.ID == connid);
            if (model != null)
            {
                ResultModel result = new ResultModel()
                {
                    Status = 0,
                    OnlineUser = OnlineUser.FindAll(u => u.GroupName == model.GroupName)
                };
                Clients.Group(model.GroupName).GetUsersResponse(result);
            }
            return base.OnDisconnected(stopCalled);
        }
    }

    public class UserModel
    {
        [JsonProperty("id")]
        public string ID { get; set; }

        [JsonProperty("userName")]
        public string UserName { get; set; }

        [JsonProperty("password")]
        public string Password { get; set; }

        [JsonProperty("groupName")]
        public string GroupName { get; set; }
    }

    public class ResultModel
    {
        [JsonProperty("status")]
        public int Status { get; set; }

        [JsonProperty("message")]
        public string Message { get; set; }

        [JsonProperty("onlineUser")]
        public List<UserModel> OnlineUser { get; set; }
    }
}

7. 运行服务端,记住地址,配置到下面步骤的客户端中

二、创建客户端

1. 在vs中创建一个空的“web应用程序”,命名为“SignalRClient”

2. 把服务端的Scripts文件夹剪切过来

3. 新建HTML页,命名为“index.html”,代码如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>client - demo</title>
</head>
<body>
    <div>
        <div>
            <input id="txtUserName" type="text" placeholder="账号" />
            <input id="txtPasswod" type="password" placeholder="密码" /> <input id="btnLogin" type="button" value="登录" />
            <br />
            <br />
            <input id="txtMessage" type="text" placeholder="消息" /> <input id="btnSend" type="button" value="发送" />
        </div>
        <ul id="usersList">
        </ul>
        <ul id="messagesList">
        </ul>
    </div>

    <script src="Scripts/jquery-1.6.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.4.2.min.js"></script>
    <script type="text/javascript">
        $(function () {
            var connection = $.hubConnection("http://localhost:52157/signalr", { useDefaultPath: false }); //注意修改为你的服务端地址
            var contosoChatHubProxy = connection.createHubProxy('demoChatHub');
            connection.start()
                .done(function () { console.log('Now connected, connection ID=' + connection.id); })
                .fail(function () { console.log('Could not connect'); });

            //-----登录-----
            document.getElementById("btnLogin").addEventListener("click", function (event) {
                var username = $("#txtUserName").val();
                var password = $("#txtPasswod").val();
                contosoChatHubProxy.invoke('login', username, password).done(function () {
                    console.log('Invocation of login succeeded');
                }).fail(function (error) {
                    console.log('Invocation of login failed. Error: ' + error);
                });
                event.preventDefault();
            });
            contosoChatHubProxy.on('LoginResponse', function (res) {
                if (res && res.status == 0) {
                    alert(res.message);
                    getUsers(contosoChatHubProxy);
                } else {
                    alert('登录失败!');
                }
            });
            //-----登录-----

            //-----消息-----
            document.getElementById("btnSend").addEventListener("click", function (event) {
                var username = $("#txtUserName").val();
                var message = $("#txtMessage").val();
                contosoChatHubProxy.invoke('sendMessage', username, message).done(function () {
                    console.log('Invocation of sendMessage succeeded');
                }).fail(function (error) {
                    console.log('Invocation of sendMessage failed. Error: ' + error);
                });
                event.preventDefault();
            });
            contosoChatHubProxy.on('SendMessageResponse', function (res) {
                if (res && res.status == 0) {
                    var li = document.createElement("li");
                    li.textContent = res.message;
                    document.getElementById("messagesList").appendChild(li);
                } else {
                    alert(res.message);
                }
            });
            //-----消息-----

            //获取在线用户
            function getUsers(contosoChatHubProxy) {
                contosoChatHubProxy.invoke('getUsers').done(function () {
                    console.log('Invocation of getUsers succeeded');
                }).fail(function (error) {
                    console.log('Invocation of getUsers failed. Error: ' + error);
                });
                contosoChatHubProxy.on('GetUsersResponse', function (res) {
                    if (res && res.status == 0) {
                        var _html = '<li>在线用户:</li>';
                        for (var i = 0; i < res.onlineUser.length; i++) {
                            _html += `<li>${res.onlineUser[i].userName}</li>`;
                        }
                        document.getElementById("usersList").innerHTML = _html;
                    }
                });
            }
        });
    </script>
</body>
</html>

4. 右键“在浏览器中查看”,交互效果如下:

 (test1和test4是一组的,注意它们的变化;test5的组只有它一个,所以它收不到其它用户的消息)

本文代码:https://files.cnblogs.com/files/shousiji/SignalRDemo_net4.rar

原文地址:https://www.cnblogs.com/shousiji/p/15124564.html