.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;width:60%;min-width: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         width: 90px;
110         color: #99a9bf;
111     }
112 
113     .demo-table-expand .el-form-item {
114         margin-right: 0;
115         margin-bottom: 0;
116         width: 30%;
117     }
118 
119     .EXC {
120         color: red;
121     }
122 </style>

猜你喜欢

转载自www.cnblogs.com/zhangjd/p/11332849.html