[Azure雲端服務及應用開發]_創建跨各種應用都能調用使用的Class Library測試SignalR Hub連線_part2
在很久之前有分享如何創建自己的Class Library(dll)
因此這次就不再多贅述
在同樣一方案(solution)下新增Class Library 專案
選.net standard版
預設產生好的專案刪除掉
建立一個Services Folder
新增一個Interface 名稱IChatService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace KYChat.Core.Services { public interface IChatService { bool IsConnected { get; } string ConnectionToken { get; set; } Task InitAsync(string userId); } } |
每組連線是否有連接成功
每組連線唯一識別的token
非同步初始化
再新增3個Class
一個Config
用來做組態設定
(for Endpoint切換)
1 2 3 4 5 6 7 8 9 10 11 12 | using System; using System.Collections.Generic; using System.Text; namespace KYChat.Core { public static class Config { public static string MainEndPoint = "http://localhost:7071"; public static string NegotiateEndPoint = $"{MainEndPoint}/api/negotiate"; } } |
一個是放在Models目錄下的
ConnectionInfo
可以直接複製Postman的json資料(可以看到有url跟token)
然後在vs當中的Edit選擇性貼上(用json)在將類別名稱修改一下
即可快速把類別創建完
之後的
ChatService
去實作IChatService介面具體任務
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | using System; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; namespace KYChat.Core.Services { public class ChatService : IChatService { public bool IsConnected { get; set; } public string ConnectionToken { get; set; } private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); private HttpClient httpClient; public async Task InitAsync(string userId) { await semaphoreSlim.WaitAsync(); if(httpClient == null) { httpClient = new HttpClient(); } var result = await httpClient.GetStringAsync($"{Config.NegotiateEndPoint}/{userId}"); semaphoreSlim.Release(); } } } |
由於 await 後頭的程式碼,不保證做的任務都會在同一個 Thread 中執行(非同步)。
因此才需要類似用lock的機制(同一個時間點我希望只會有一個程序可存取)
去做控管
再此用SemaphoreSlim 來完成跨執行緒的鎖定機制,指定可同時
授與信號並行的初始要求數目及最大數目,在此都先設置為1。
接著至Nuget套件下載安裝
Newton Json
到ChatService我們撰寫藉由JsonConvert獲取反序列回來的連線資訊
透過http client GetStringAsync 取得Config戳打Negotiate End Point結果
在將其json結果給反序列成程式後端較好處理的.Net物件資料型態
再來安裝我們的另一套件
Microsoft.AspNetCore.SignalR.Client
然後補撰寫設計好的剩餘片段
建立Hub相關的程式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | using KYChat.Core.Models; using Microsoft.AspNetCore.SignalR.Client; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; namespace KYChat.Core.Services { public class ChatService : IChatService { public bool IsConnected { get; set; } public string ConnectionToken { get; set; } private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); private HttpClient httpClient; HubConnection hub; public async Task InitAsync(string userId) { await semaphoreSlim.WaitAsync(); if(httpClient == null) { httpClient = new HttpClient(); } var result = await httpClient.GetStringAsync($"{Config.NegotiateEndPoint}/{userId}"); var info = JsonConvert.DeserializeObject<ConnectionInfo>(result); var connectionBuilder = new HubConnectionBuilder(); connectionBuilder.WithUrl(info.Url, (obj) => { obj.AccessTokenProvider = () => Task.Run(() => info.AccessToken); }); hub = connectionBuilder.Build(); await hub.StartAsync(); ConnectionToken = hub.ConnectionId; IsConnected = true; semaphoreSlim.Release(); } } } |
在Interface當中補上實作斷線操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; namespace KYChat.Core.Services { public interface IChatService { bool IsConnected { get; } string ConnectionToken { get; set; } Task InitAsync(string userId); Task DisconnectionAsync(); } } |
之後補實作
async Task DisconnectionAsync()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | using KYChat.Core.Models; using Microsoft.AspNetCore.SignalR.Client; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Net.Http; using System.Text; using System.Threading; using System.Threading.Tasks; namespace KYChat.Core.Services { public class ChatService : IChatService { public bool IsConnected { get; set; } public string ConnectionToken { get; set; } private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); private HttpClient httpClient; HubConnection hub; public async Task InitAsync(string userId) { await semaphoreSlim.WaitAsync(); if (httpClient == null) { httpClient = new HttpClient(); } var result = await httpClient.GetStringAsync($"{Config.NegotiateEndPoint}/{userId}"); var info = JsonConvert.DeserializeObject<ConnectionInfo>(result); var connectionBuilder = new HubConnectionBuilder(); connectionBuilder.WithUrl(info.Url, (obj) => { obj.AccessTokenProvider = () => Task.Run(() => info.AccessToken); }); hub = connectionBuilder.Build(); await hub.StartAsync(); ConnectionToken = hub.ConnectionId; IsConnected = true; semaphoreSlim.Release(); } public async Task DisconnectionAsync() { if (!IsConnected) return; try { await hub.DisposeAsync(); } catch (Exception ex) { Debug.WriteLine(ex); } IsConnected = false; } } } |
對方案新建一個.net core Console App 用來模擬client
(由於console較輕量便利因此一開始先用這專案類型做連線測試)
對.net core Console專案右鍵添加參考
選取Chat.Core的Class Library專案
接著撰寫調用封裝好的dll中程式相關物件跟函數
大致寫完Client端模擬連線到Hub的過程之後
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | using KYChat.Core.Services; using System; using System.Threading.Tasks; namespace KYChat.Client { class Program { static ChatService myService; static string userName; static async Task Main(string[] args) { Console.WriteLine("User Name:"); userName = Console.ReadLine(); myService = new ChatService(); await myService.InitAsync(userName); Console.WriteLine("You are connected now !!"); bool IsKeepGoing = true; do { var text = Console.ReadLine(); if (text.Trim().ToLower().Equals("exit")) { await myService.DisconnectionAsync(); IsKeepGoing = false; } } while (IsKeepGoing); } } } |
在此我們執行看看(這裡可到方案右鍵屬性設置改為多個專案初始執行)
這裡可注意到一次開啟兩隻不同種類專案的程式
先等Azure 被動監聽的常駐程式開啟執行後
對.net core console project輸入user id
隨意輸入名字之類的
就可看到連線成功~
留言
張貼留言