.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>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM