.net core 使用SignalR实现实时通信
Posted zhangjd
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.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>
以上是关于.net core 使用SignalR实现实时通信的主要内容,如果未能解决你的问题,请参考以下文章
ASP.NET Core的实时库: SignalR简介及使用
ASP.NET Core SignalR实时推送配置,业务层实时推送SignalR消息