zoukankan      html  css  js  c++  java
  • .net core 使用SignalR实现实时通信

    这几天在研究SignalR,网上大部分的例子都是聊天室,我的需求是把服务端的信息发送给前端展示。并且需要实现单个用户推送。

    用户登录我用的是ClaimsIdentity,这里就不多解释,如果不是很了解,可以看这篇文章https://www.cnblogs.com/zhangjd/p/11332558.html

    推荐https://www.cnblogs.com/laozhang-is-phi/p/netcore-vue-signalr.html#tbCommentBody这个博客,写的很详细,并且附有Dome

    一、后端实现

    1、引用SignalR包

    Install-Package Microsoft.AspNetCore.SignalR

    2、声明一个类来记录用户的连接信息。

    1     public class SignalRModel
    2     {
    3         public static Dictionary<string, SignalRStatus> StaticList = new Dictionary<string, SignalRStatus>();
    4         public static Dictionary<string, string> SignalRList { get; set; } = new Dictionary<string, string>();
    5     }

    3、声明Hub,这里我重写了连接和断开方法,用来绑定用户和连接的ConnectionId。(这个比较复杂,是因为我程序中执行的第三方程序,需要实时输出当前执行的程序的日志。但是调用的执行不可能直接写在控制器里,这样调用我没办法获取当前用户的登录Id。然后我就在发起连接和断开连接的方法处理了。)

     1 public class ChatHub : Hub
     2     {
     3         /// <summary>
     4         /// 连接成功
     5         /// </summary>
     6         /// <returns></returns>
     7         public override Task OnConnectedAsync()
     8         {
     9             var id = this.Context.ConnectionId;
    10             var claimNameIdentifier = this.Context.User.Claims.FirstOrDefault(s => s.Type == ClaimTypes.NameIdentifier)?.Value;
    11             SignalRModel.SignalRList.Add(id, claimNameIdentifier);
    12             if (SignalRModel.StaticList.Any(s => s.Key.Equals(claimNameIdentifier)))
    13             {
    14                 SignalRModel.StaticList.Remove(claimNameIdentifier);
    15             }
    16             SignalRModel.StaticList.Add(claimNameIdentifier, SignalRStatus.Open);
    17             return base.OnConnectedAsync();
    18         }
    19         /// <summary>
    20         /// 断开连接
    21         /// </summary>
    22         public override Task OnDisconnectedAsync(Exception exception)
    23         {
    24             var id = this.Context.ConnectionId;
    25             var claimNameIdentifier = this.Context.User.Claims.FirstOrDefault(s => s.Type == ClaimTypes.NameIdentifier)?.Value;
    26             SignalRModel.SignalRList.Remove(id);
    27             SignalRModel.StaticList.Remove(claimNameIdentifier);
    28             return base.OnDisconnectedAsync(exception);
    29         }
    30         /// <summary>
    31         /// 发送消息
    32         /// </summary>
    33         /// <param name="user"></param>
    34         /// <param name="message"></param>
    35         /// <returns></returns>
    36         public async Task SendMessage(string user, string message)
    37         {
    38             await Clients.All.SendAsync("ReceiveMessage", user, message);
    39         }
    40     }

    4、在程序启动的时候,把记录用户连接信息的类,注入成单例,保存用户和连接的对应关系,方便单个通信。

    1   services.AddSingleton<SignalRModel>(provider =>
    2   {
    3       return new SignalRModel();
    4   });

    5、配置

     1)、在ConfigureServices中加入

    services.AddSignalR();//要写在addmvc()前面
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

     2)、在Configure中加入

    app.UseMvc();
    app.UseSignalR(routes => { routes.MapHub<ChatHub>("/api/chatHub"); });//要写在UseMvc后面

    6、这里我写了后端两个接口来发送消息,区别在于第一个是群发,第二个是针对一个连接发送的。

     1         [HttpGet("SendAll")]
     2         public IActionResult SendAll()
     3         {
     4             _hubContext.Clients.All.SendAsync("ReceiveUpdate", "推送全部人").Wait();
     5             return Ok("推送全部人");
     6         }
     7         [HttpGet("SendOnly")]
     8         public IActionResult SendOnly()
     9         {
    10             var claimNameIdentifier = User.Claims.FirstOrDefault(s => s.Type == ClaimTypes.NameIdentifier)?.Value;
    11             if (string.IsNullOrEmpty(claimNameIdentifier))
    12             {
    13                 return Ok(new { code = ResultCode.NotLogin, message = "用户未登陆!" });
    14             }
    15             _hubContext.Clients.Clients(claimNameIdentifier).SendAsync("ReceiveUpdate", DateTime.Now).Wait();
    16             return Ok("推送当前登录用户");
    17         }

    7、我项目实际用到的是这样的,给当前登录用户发送日志消息,判断连接是否断开,如果断开需要获取前面写的日志,发送给前端之后,把连接的状态改成连接中,后面就正常发送。

     1    foreach (var item in SignalRModel.SignalRList.Where(s => s.Value.Equals(userId.ToString())).ToList())
     2    {
     3       if (SignalRModel.StaticList.Any(s => s.Key.Equals(userId.ToString()) && s.Value == SignalRStatus.Open))
     4       {
     5          if (SignalRModel.StaticList.Any(s => s.Key.Equals(userId.ToString())))
     6          {
     7               SignalRModel.StaticList.Remove(userId.ToString());
     8          }
     9          SignalRModel.StaticList.Add(userId.ToString(), SignalRStatus.working);
    10          _hubContext.Clients.Client(item.Key).SendAsync("ReceiveUpdate", FileHelper.ReadFile(Path.Combine(filePath, "tls_simplify.txt"), Encoding.UTF8)).Wait();
    11       }
    12       _hubContext.Clients.Client(item.Key).SendAsync("ReceiveUpdate", args.Data).Wait();
    13    }

    二、前端vue

    1、安装依赖包

    npm install @aspnet/signalr

    2、示例页面

      1 <template>
      2     <section>
      3         <div style="display: none1">
      4             <el-form ref="form" label-width="80px" @submit.prevent="onSubmit"
      5                      style="margin:20px;60%;min-600px;">
      6                 <el-form-item label="用户名">
      7                     <el-input v-model="userName"></el-input>
      8                 </el-form-item>
      9                 <el-form-item label="密码">
     10                     <el-input v-model="userMessage"></el-input>
     11                 </el-form-item>
     12             </el-form>
     13             <ul v-for="(item, index) in messages" v-bind:key="index + 'itemMessage'">
     14                 <li><b>Name: </b>{{item.user}}</li>
     15                 <li><b>Message: </b>{{item.message}}</li>
     16             </ul>
     17             <p>
     18             <b>后台发送消息: </b>{{this.postMessage}}
     19             </p>
     20             <el-button type="primary" @click="submitCard">登录</el-button>
     21             <el-button type="primary" @click="getLogs">查询</el-button>
     22         </div>
     23     </section>
     24 </template>
     25 
     26 <script>
     27    
     28     import * as signalR from "@aspnet/signalr";
     29 
     30     export default {
     31       name: 'Dashboard',
     32         data() {
     33             return {
     34                 filters: {
     35                     LinkUrl: ''
     36                 },
     37                 listLoading: true,
     38                 postMessage: "",
     39                 userName: "Tom",
     40                 userMessage: "123",
     41                 connection: "",
     42                 messages: [],
     43                 t: ""
     44 
     45             }
     46         },
     47         methods: {
     48             getRoles() {
     49                 let thisvue=this;
     50                 let para = {
     51                     page: this.page,
     52                     key: this.filters.LinkUrl
     53                 };
     54                 this.listLoading = true;
     55                 thisvue.connection.start().then(() => {
     56                    thisvue.connection.invoke('GetLatestCount', 1).catch(function (err) {
     57                    return console.error(err);
     58                 });
     59             });
     60             },
     61             submitCard: function () {
     62                 if (this.userName && this.userMessage) {
     63                     this.connection.invoke('SendMessage', this.userName, this.userMessage).catch(function (err) {
     64                         return console.error(err);
     65                     });
     66 
     67                 }
     68             },
     69             getLogs: function () {
     70                 this.listLoading = true;
     71                 this.connection.invoke('GetLatestCount', 1).catch(function (err) {
     72                     return console.error(err);
     73                 });
     74             }
     75         },
     76         created: function () {
     77             let thisVue = this;
     78             thisVue.connection = new signalR.HubConnectionBuilder()
     79                 .withUrl('http://localhost:5000/api/chatHub')
     80                 .configureLogging(signalR.LogLevel.Information)
     81                 .build();
     82             thisVue.connection.on('ReceiveMessage', function (user, message) {
     83                 thisVue.messages.push({user, message});
     84             });
     85 
     86             thisVue.connection.on('ReceiveUpdate', function (update) {
     87                 console.info('update success!')
     88                 thisVue.listLoading = false;
     89                 thisVue.postMessage = update;
     90                 window.clearInterval(this.t)
     91             })
     92         },
     93         mounted() {
     94             this.getRoles();
     95         },
     96         beforeDestroy() {
     97             window.clearInterval(this.t)
     98             this.connection.stop();
     99         }
    100     }
    101 </script>
    102 
    103 <style scoped>
    104     .demo-table-expand {
    105         font-size: 0;
    106     }
    107 
    108     .demo-table-expand label {
    109          90px;
    110         color: #99a9bf;
    111     }
    112 
    113     .demo-table-expand .el-form-item {
    114         margin-right: 0;
    115         margin-bottom: 0;
    116          30%;
    117     }
    118 
    119     .EXC {
    120         color: red;
    121     }
    122 </style>
  • 相关阅读:
    重新整理数据结构与算法—— 斐波那契二分查找法[十四]
    重新整理数据结构与算法—— 插值二分查找法[十三]
    重新整理数据结构与算法—— 二分查找法[十二]
    Tuple和ValueTuple
    安装 php_mongodb.dll的坑
    前端——localStorage详细总结
    前端——Vue.js学习总结一
    数据库SQL语句大全,最常用的SQL语句
    Java——多线程超详细总结
    Java——异常那些事
  • 原文地址:https://www.cnblogs.com/zhangjd/p/11332849.html
Copyright © 2011-2022 走看看