zoukankan      html  css  js  c++  java
  • .NET Core使用SignalR做登录、推送

    过多介绍我就不叙述了,奉上官方文档

    本文使用的是.NET Core 3.1版本,创建SignalR的服务端和客户端,结构如下:

     

    一、创建服务端

    VS2019创建一个空的Web应用程序,命名为SignalRServer

    1. 添加ResultModel类:

        public class ResultModel
        {
            public int Status { get; set; }
            public string Message { get; set; }
            public List<UserModel> OnlineUser { get; set; }
        }

    2.  添加UserModel类:

        public class UserModel
        {
            public string ID { get; set; }
            public string UserName { get; set; }
            public string Password { get; set; }
            public string GroupName { get; set; }
        }

    3.  添加ChatHub类:

      1     public class ChatHub : Hub
      2     {
      3         /// <summary>
      4         /// 已登录的用户信息
      5         /// </summary>
      6         public static List<UserModel> OnlineUser { get; set; } = new List<UserModel>();
      7 
      8         /// <summary>
      9         /// 模拟存放在数据库里的用户信息
     10         /// </summary>
     11         private static readonly List<UserModel> _dbuser = new List<UserModel> {
     12           new UserModel{
     13             UserName = "test1", Password = "111", GroupName = "Group1"
     14           },
     15           new UserModel{
     16             UserName = "test2", Password = "111", GroupName = "Group2"
     17           },
     18           new UserModel{
     19             UserName = "test3", Password = "111", GroupName = "Group2"
     20           },
     21           new UserModel{
     22             UserName = "test4", Password = "111", GroupName = "Group1"
     23           },
     24           new UserModel{
     25             UserName = "test5", Password = "111", GroupName = "Group3"
     26           },
     27         };
     28 
     29         /// <summary>
     30         /// 登录验证
     31         /// </summary>
     32         public async Task Login(string username, string password)
     33         {
     34             string connid = Context.ConnectionId;
     35             ResultModel result = new ResultModel
     36             {
     37                 Status = 0,
     38                 Message = "登录成功!"
     39             };
     40             if (!OnlineUser.Exists(u => u.ID == connid))
     41             {
     42                 var model = _dbuser.Find(u => u.UserName == username && u.Password == password);
     43                 if (model != null)
     44                 {
     45                     model.ID = connid;
     46                     OnlineUser.Add(model);
     47                     //给当前的连接分组
     48                     await Groups.AddToGroupAsync(connid, model.GroupName);
     49                 }
     50                 else
     51                 {
     52                     result.Status = 1;
     53                     result.Message = "账号或密码错误!";
     54                 }
     55             }
     56             //给当前连接返回消息
     57             await Clients.Client(connid).SendAsync("LoginResponse", result);
     58         }
     59 
     60         /// <summary>
     61         /// 获取所在组的在线用户
     62         /// </summary>
     63         public async Task GetUsers()
     64         {
     65             var model = OnlineUser.Find(u => u.ID == Context.ConnectionId);
     66             ResultModel result = new ResultModel();
     67             if (model == null)
     68             {
     69                 result.Status = 1;
     70                 result.Message = "请先登录!";
     71             }
     72             else
     73             {
     74                 result.OnlineUser = OnlineUser.FindAll(u => u.GroupName == model.GroupName);
     75             }
     76             //给所在组返回消息
     77             await Clients.Group(model.GroupName).SendAsync("GetUsersResponse", result);
     78         }
     79 
     80         public async Task SendMessage(string user, string message)
     81         {
     82             ResultModel result = new ResultModel();
     83             var model = OnlineUser.Find(u => u.ID == Context.ConnectionId);
     84             if (model == null)
     85             {
     86                 result.Status = 1;
     87                 result.Message = "请先登录!";
     88             }
     89             else
     90             {
     91                 result.Status = 0;
     92                 result.Message = $"“{user}”发送的消息:{message}";
     93             }
     94             await Clients.Group(model.GroupName).SendAsync("SendMessageResponse", result);
     95         }
     96 
     97         public override Task OnConnectedAsync()
     98         {
     99             return base.OnConnectedAsync();
    100         }
    101 
    102         /// <summary>
    103         /// 当连接断开时的处理
    104         /// </summary>
    105         public override Task OnDisconnectedAsync(Exception exception)
    106         {
    107             string connid = Context.ConnectionId;
    108             var model = OnlineUser.Find(u => u.ID == connid);
    109             int count = OnlineUser.RemoveAll(u => u.ID == connid);
    110             if (model != null)
    111             {
    112                 ResultModel result = new ResultModel()
    113                 {
    114                     Status = 0,
    115                     OnlineUser = OnlineUser.FindAll(u => u.GroupName == model.GroupName)
    116                 };
    117                 Clients.Group(model.GroupName).SendAsync("GetUsersResponse", result);
    118             }
    119             return base.OnDisconnectedAsync(exception);
    120         }
    121     }

    可以重写基类的两个方法:

    OnConnectedAsync 当连接成功时执行
    OnDisconnectedAsync 当连接断开时执行,关闭、刷新 浏览器或标签页都会执行

    服务端发送给客户端的信息,我是通过分组推送,就是在同一个组里的用户才会收到(类似群聊):

    Clients.Group(groupName).SendAsync();

    也可以所有在线用户都收到:

    Clients.All.SendAsync();

    4. Startup类的配置如下:

     1     public class Startup
     2     {
     3         // This method gets called by the runtime. Use this method to add services to the container.
     4         // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
     5         public void ConfigureServices(IServiceCollection services)
     6         {
     7             services.AddCors(options => options.AddPolicy("CorsPolicy",
     8             builder =>
     9             {
    10                 builder.AllowAnyMethod().AllowAnyHeader()
    11                        .WithOrigins("https://localhost:44395")
    12                        .AllowCredentials();
    13             }));
    14 
    15             services.AddSignalR();
    16         }
    17 
    18         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    19         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    20         {
    21             if (env.IsDevelopment())
    22             {
    23                 app.UseDeveloperExceptionPage();
    24             }
    25 
    26             app.UseRouting();
    27 
    28             app.UseCors("CorsPolicy");
    29 
    30             app.UseEndpoints(endpoints =>
    31             {
    32                 endpoints.MapGet("/", async context =>
    33                 {
    34                     await context.Response.WriteAsync("Hello World!");
    35                 });
    36                 endpoints.MapHub<ChatHub>("/chatHub");
    37             });
    38         }
    39     }

    注意:跨域是要指定地址的,不能是全部,否则客户端无法连接;当前你先留空,等客户端建立好了记得回来修改。

    二、创建客户端

    VS2019创建一个空的Web应用程序,命名为SignalRClient

    1. 新建文件夹,命名为wwwroot(如果不是这个名字,需要在中间件中指定了)

    2. chat.js代码如下,注意withUrl中的地址,改为你的SignalRServer实际运行成功后的地址

     1 "use strict";
     2 
     3 var connection = new signalR.HubConnectionBuilder()
     4     .withUrl("https://localhost:44317/chatHub")
     5     .withAutomaticReconnect() //断线自动重连
     6     .build();
     7 
     8 connection.start();
     9 
    10 //自动重连成功后的处理
    11 connection.onreconnected(connectionId => {
    12     alert(connectionId);
    13 });
    14 
    15 
    16 //---消息---
    17 document.getElementById("sendButton").addEventListener("click", function (event) {
    18     var user = document.getElementById("userInput").value;
    19     var message = document.getElementById("messageInput").value;
    20     connection.invoke("SendMessage", user, message).catch(function (err) {
    21         return console.error(err.toString());
    22     });
    23     event.preventDefault();
    24 });
    25 
    26 connection.on("SendMessageResponse", function (res) {
    27     if (res && res.status == 0) {
    28         var li = document.createElement("li");
    29         li.textContent = res.message;
    30         document.getElementById("messagesList").appendChild(li);
    31     } else {
    32         alert(res.message);
    33     }
    34 });
    35 //---消息---
    36 
    37 
    38 //---登录---
    39 document.getElementById("btnLogin").addEventListener("click", function (event) {
    40     var user = document.getElementById("userInput").value;
    41     var message = document.getElementById("messageInput").value;
    42     connection.invoke("Login", user, message).catch(function (err) {
    43         return console.error(err.toString());
    44     });
    45     event.preventDefault();
    46 });
    47 
    48 connection.on("LoginResponse", function (res) {
    49     if (res && res.status == 0) {
    50         sessionStorage.setItem('curuser', res.data);
    51         alert(res.message);
    52         getUsers();
    53     }
    54     else {
    55         alert('登录失败!');
    56     }
    57 });
    58 //---登录---
    59 
    60 
    61 //获取在线用户
    62 function getUsers() {
    63     connection.invoke("GetUsers").catch(function (err) {
    64         return console.error(err.toString());
    65     });
    66     connection.on("GetUsersResponse", function (res) {
    67         if (res && res.status == 0) {
    68             var _lis = '<li>在线用户:</li>';
    69             for (var i = 0; i < res.onlineUser.length; i++) {
    70                 _lis += `<li>${res.onlineUser[i].userName}</li>`;
    71             }
    72             document.getElementById("usersList").innerHTML = _lis;
    73         }
    74     });
    75 }

    3. signalrjs库,你可以自行下载;或者“项目右键 -> 添加 -> 客户端库”;

        提供程序改为“unpkg”,输入“@microsoft/signalr”按回车搜索,勾选自己需要的,最后注意存放的位置

        

    4. 创建页面:新建一个HomeController,添加视图,去掉布局页勾选

    Index.cshtml代码如下:

     1 @{
     2     Layout = null;
     3 }
     4 
     5 <!DOCTYPE html>
     6 
     7 <html>
     8 <head>
     9     <meta name="viewport" content="width=device-width" />
    10     <title>Index</title>
    11 </head>
    12 <body>
    13     <div>
    14         <input id="userInput" type="text" /><br />
    15         <input id="messageInput" type="text" /><br />
    16         <input id="btnLogin" type="button" value="登录" />
    17         <input id="sendButton" type="button" value="发送" />
    18     </div>
    19     <ul id="usersList">
    20     </ul>
    21     <ul id="messagesList">
    22     </ul>
    23     <script src="~/lib/signalr/signalr.js"></script>
    24     <script src="~/js/chat.js"></script>
    25 </body>
    26 </html>

    5. Startup类的配置如下:

     1     public class Startup
     2     {
     3         // This method gets called by the runtime. Use this method to add services to the container.
     4         // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
     5         public void ConfigureServices(IServiceCollection services)
     6         {
     7             services.AddControllersWithViews();
     8         }
     9 
    10         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    11         public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    12         {
    13             if (env.IsDevelopment())
    14             {
    15                 app.UseDeveloperExceptionPage();
    16             }
    17             app.UseStaticFiles();
    18 
    19             app.UseRouting();
    20 
    21             app.UseEndpoints(endpoints =>
    22             {
    23                 endpoints.MapControllerRoute(
    24                     name: "default",
    25                     pattern: "{controller=Home}/{action=Index}/{id?}");
    26             });
    27         }
    28     }

    服务端跑起来,客户端跑起来,最后来看下效果:

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


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

  • 相关阅读:
    数据结构2_java---栈,括号匹配
    数据结构1_java---单链表的操作,约瑟夫问题
    算法---ALGO-3 Java K好数 蓝桥杯
    算法---区间K大数查找 Java 蓝桥杯ALGO-1
    算法---进制转换,八进制,十六进制,二进制
    实用---java保留小数点后位数以及输出反转数字
    实用---生命游戏 Java
    实用---GUI的搭建,windowbuilder的使用
    如今大火的算法框架TensorFlow,都有哪些值得一看的好书呢?
    如今大火的算法框架TensorFlow,都有哪些值得一看的好书呢?
  • 原文地址:https://www.cnblogs.com/shousiji/p/12737925.html
Copyright © 2011-2022 走看看