仿微信 即时聊天工具 - SignalR (一)
話不多說,先上圖
?
?
?
?
?
?
背景:
微信聊天,經常會遇見視頻發不了,嗯,還有聊天不方便的問題,于是我就自己買了服務器,部署了一套可以直接在微信打開的網頁進行聊天,這樣只需要發送個url給朋友,就能聊天了!
由于自己無聊弄著玩的,代碼比較粗糙,各位多指正!
1、首先安裝SignalR,這步我就不做過多說明了
安裝好以后在根目錄新建一個Hubs文件夾,做用戶的注冊和通知
MessageHub.cs 文件
| 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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | using?Microsoft.AspNet.SignalR; using?Microsoft.AspNet.SignalR.Hubs; using?System; using?System.Collections; using?System.Collections.Generic; using?System.Linq; using?System.Threading; using?System.Threading.Tasks; using?System.Web; ? namespace?SignalR.Hubs { ????[HubName("MessageHub")] ????public?class?MessageHub : Hub ????????{ ????????????private?readonly?ChatTicker ticker; ????????????public?MessageHub() ????????????{ ????????????????ticker = ChatTicker.Instance; ????????????} ? ????????????public?void?register(string?username,?string?group?=?"default") ????????????{ ????????????????var?list = (List<SiginalRModel>)HttpRuntime.Cache.Get("msg_hs"); ????????????????if?(list ==?null) ????????????????{ ????????????????????list =?new?List<SiginalRModel>(); ????????????????} ???????????????? ? ????????????????if?(list.Any(x => x.connectionId == Context.ConnectionId)) ????????????????{ ????????????????????Clients.Client(Context.ConnectionId).broadcastMessage("已經注冊,無需再次注冊"); ????????????????} ????????????else?if?(list.Any(x => x.name == username)) ????????????{ ????????????????var?model = list.Where(x => x.name == username && x.group?==?group).FirstOrDefault(); ????????????????if?(model !=?null) ????????????????{ ????????????????????//注冊到全局? ????????????????????ticker.GlobalContext.Groups.Add(Context.ConnectionId,?group); ????????????????????Clients.Client(model.connectionId).exit(); ????????????????????ticker.GlobalContext.Groups.Remove(model.connectionId,?group); ????????????????????list.Remove(model); ????????????????????model.connectionId = Context.ConnectionId; ????????????????????list.Add(model); ????????????????????Clients.Group(group).removeUserList(model.connectionId); ????????????????????Thread.Sleep(200); ????????????????????var?gourpList = list.Where(x => x.group?==?group).ToList(); ????????????????????Clients.Group(group).appendUserList(Context.ConnectionId, gourpList); ????????????????????HttpRuntime.Cache.Insert("msg_hs", list); ????????????????????// Clients.Client(model.connectionId).broadcastMessage("名稱重復,只能注冊一個"); ????????????????} ????????????????//Clients.Client(Context.ConnectionId).broadcastMessage("名稱重復,只能注冊一個"); ????????????} ????????????else ????????????????{ ????????????????????list.Add(new?SiginalRModel() { name = username,?group?=?group, connectionId = Context.ConnectionId }); ? ????????????????????//注冊到全局? ????????????????????ticker.GlobalContext.Groups.Add(Context.ConnectionId,?group); ????????????????????Thread.Sleep(200); ? ????????????????????var?gourpList = list.Where(x => x.group?==?group).ToList(); ????????????????????Clients.Group(group).appendUserList(Context.ConnectionId, gourpList); ????????????????????HttpRuntime.Cache.Insert("msg_hs", list); ????????????????} ? ????????????} ? ????????????public?void?Say(string?msg) ????????????{ ????????????????var?list = (List<SiginalRModel>)HttpRuntime.Cache.Get("msg_hs"); ????????????????if?(list ==?null) ????????????????{ ????????????????????list =?new?List<SiginalRModel>(); ????????????????} ????????????????var?userModel = list.Where(x => x.connectionId == Context.ConnectionId).FirstOrDefault(); ????????????????if?(userModel !=?null?) ????????????????{ ????????????????????Clients.Group(userModel.group).Say(userModel.name, msg); ????????????????} ????????????} ? ????????public?void?Exit() ????????{ ????????????OnDisconnected(true); ????????} ? ????????public?override?Task OnDisconnected(bool?s) ????????????????{ ????????????????????var?list = (List<SiginalRModel>)HttpRuntime.Cache.Get("msg_hs"); ????????????????????if?(list ==?null) ????????????????????{ ????????????????????????list =?new?List<SiginalRModel>(); ????????????????????} ????????????????????var?closeModel = list.Where(x => x.connectionId == Context.ConnectionId).FirstOrDefault(); ? ????????????????????if?(closeModel !=?null) ????????????????????{ ????????????????????????list.Remove(closeModel); ? ????????????????????????Clients.Group(closeModel.group).removeUserList(Context.ConnectionId); ? ?????????????????????} ????????????????????HttpRuntime.Cache.Insert("msg_hs", list); ????????????????? ????????????????????return?base.OnDisconnected(s); ????????????????} ????????????} ????????? ? ????public?class?ChatTicker ????????{ ????????????#region 實現一個單例 ? ????????????private?static?readonly?ChatTicker _instance = ????????????????new?ChatTicker(GlobalHost.ConnectionManager.GetHubContext<MessageHub>()); ? ????????????private?readonly?IHubContext m_context; ? ????????????private?ChatTicker(IHubContext context) ????????????{ ? ????????????????m_context = context; ????????????????//這里不能直接調用Sender,因為Sender是一個不退出的“死循環”,否則這個構造函數將不會退出。? ????????????????//其他的流程也將不會再執行下去了。所以要采用異步的方式。? ????????????????//Task.Run(() => Sender()); ????????????} ? ????????????public?IHubContext GlobalContext ????????????{ ????????????????get?{?return?m_context; } ????????????} ? ????????????public?static?ChatTicker Instance ????????????{ ????????????????get?{?return?_instance; } ????????????} ? ????????????#endregion ????????} ? ????public?class?SiginalRModel { ????????public?string?connectionId {?get;?set; } ? ????????public?string?group?{?get;?set; } ????????public?string?name {?get;?set; } ????} }<br data-filtered="filtered"><br data-filtered="filtered">我把類和方法都寫到一塊了,大家最好是分開! |
?
接下來是控制器
HomeController.cs
| 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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | using?Microsoft.AspNet.SignalR; using?Microsoft.AspNet.SignalR.Client; using?SignalR.Hubs; using?SignalR.ViewModels; using?System; using?System.Collections; using?System.Collections.Generic; using?System.IO; using?System.Linq; using?System.Web; using?System.Web.Mvc; using?Newtonsoft.Json; using?System.Diagnostics; using?System.Text.RegularExpressions; ? namespace?SignalR.Controllers { ????public?class?HomeController : Controller ????{ ????????public?ActionResult Index() ????????{ ????? ????????????return?View(); ????????} ? ? ????????public?ActionResult GetV(string?v) ????????{ ????????????if?(!string.IsNullOrEmpty(v)) ????????????{ ????????????????string?url = RedisHelper.Get(v)?.ToString(); ????????????????if?(!string.IsNullOrEmpty(url)) ????????????????{ ????????????????????return?Json(new?{ isOk =?true, m = url }, JsonRequestBehavior.AllowGet); ????????????????} ????????????????return?Json(new?{ isOk =?false}, JsonRequestBehavior.AllowGet); ????????????} ????????????return?Json(new?{ isOk =?false?}, JsonRequestBehavior.AllowGet); ????????} ? ????????public?ActionResult getkey(string?url) ????????{ ????????????if?(!string.IsNullOrEmpty(url)) ????????????{ ????????????????var?s =?"v"?+ Util.GetRandomLetterAndNumberString(new?Random(), 5).ToLower(); ????????????????var?dt = Convert.ToDateTime(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd 04:00:00")); ????????????????int?min = Convert.ToInt16((dt - DateTime.Now).TotalMinutes); ????????????????RedisHelper.Set(s, url, min); ????????????????return?Json(new?{ isOk =?true, m = s }, JsonRequestBehavior.AllowGet); ????????????} ????????????return?Json(new?{ isOk =?false?}, JsonRequestBehavior.AllowGet); ????????} ? ????????public?ActionResult upfile() ????????{ ????????????try ????????????{ ????????????????if?(Request.Files.Count > 0) ????????????????{ ????????????????????var?file = Request.Files[0]; ????????????????????if?(file !=?null) ????????????????????{ ????????????????????????var?imgList =?new?List<string>() {?".gif",?".jpg",?".bmp",?".png"?}; ????????????????????????var?videoList =?new?List<string>() {?".mp4"?}; ????????????????????????FileModel fmodel =?new?FileModel(); ? ????????????????????????string?name = Guid.NewGuid().ToString(); ????????????????????????string?fileExt = Path.GetExtension(file.FileName).ToLower();//上傳文件擴展名 ????????????????????????string?path = Server.MapPath("~/files/") + name + fileExt; ????????????????????????file.SaveAs(path); ? ????????????????????????string?extension =?new?FileInfo(path).Extension; ? ????????????????????????if?(extension ==?".mp4") ????????????????????????{ ????????????????????????????fmodel.t = 2; ????????????????????????} ????????????????????????else?if?(imgList.Contains(extension)) ????????????????????????{ ????????????????????????????fmodel.t = 1; ????????????????????????} ????????????????????????else ????????????????????????{ ????????????????????????????fmodel.t = 0; ????????????????????????} ????????????????????????string?url = Guid.NewGuid().ToString(); ????????????????????????fmodel.url =?"http://"?+ Request.Url.Host; ????????????????????????if?(Request.Url.Port != 80) ????????????????????????{ ????????????????????????????fmodel.url +=?":"?+ Request.Url.Port; ????????????????????????} ????????????????????????fmodel.url +=?"/files/"?+ name + fileExt; ????????????????????????GetImageThumb(Server.MapPath("~") +?"files\\"?+ name + fileExt, name); ????????????????????????return?Json(new?{ isOk =?true, m =?"file:"?+ JsonConvert.SerializeObject(fmodel) }, JsonRequestBehavior.AllowGet); ????????????????????} ????????????????} ????????????} ????????????catch(Exception ex) ????????????{ ????????????????Log.Info(ex); ????????????} ???????????? ???????????? ????????????return?Content(""); ????????} ? ????????public?string?GetImageThumb(string?localVideo,string?name) ????????{ ????????????string?path = AppDomain.CurrentDomain.BaseDirectory; ????????????string?ffmpegPath = path +?"/ffmpeg.exe"; ????????????string?oriVideoPath = localVideo; ????????????int?frameIndex = 5; ????????????int?_thubWidth; ????????????int?_thubHeight; ????????????GetMovWidthAndHeight(localVideo,?out?_thubWidth,?out?_thubHeight); ????????????int?thubWidth = 200; ????????????int?thubHeight = _thubWidth == 0 ? 200 : (thubWidth * _thubHeight / _thubWidth );? ????????????? ????????????string?thubImagePath = path +??"files\\"?+ name +?".jpg"; ????????????string?command =?string.Format("\"{0}\" -i \"{1}\" -ss {2} -vframes 1 -r 1 -ac 1 -ab 2 -s {3}*{4} -f image2 \"{5}\"", ffmpegPath, oriVideoPath, frameIndex, thubWidth, thubHeight, thubImagePath); ????????????Cmd.RunCmd(command); ????????????return?name; ????????} ? ????????/// <summary> ????????/// 獲取視頻的幀寬度和幀高度 ????????/// </summary> ????????/// <param name="videoFilePath">mov文件的路徑</param> ????????/// <returns>null表示獲取寬度或高度失敗</returns> ????????public?static?void?GetMovWidthAndHeight(string?videoFilePath,?out?int?width,?out?int?height) ????????{ ????????????try ????????????{ ? ????????????????//執行命令獲取該文件的一些信息 ????????????????string?ffmpegPath = AppDomain.CurrentDomain.BaseDirectory +??"/ffmpeg.exe"; ????????????????string?output; ????????????????string?error; ????????????????ExecuteCommand("\""?+ ffmpegPath +?"\""?+?" -i "?+?"\""?+ videoFilePath +?"\"",?out?output,?out?error); ????????????????if?(string.IsNullOrEmpty(error)) ????????????????{ ????????????????????width = 0; ????????????????????height = 0; ????????????????} ? ????????????????//通過正則表達式獲取信息里面的寬度信息 ????????????????Regex regex =?new?Regex("(\\d{2,4})x(\\d{2,4})", RegexOptions.Compiled); ????????????????Match m = regex.Match(error); ????????????????if?(m.Success) ????????????????{ ????????????????????width =?int.Parse(m.Groups[1].Value); ????????????????????height =?int.Parse(m.Groups[2].Value); ????????????????} ????????????????else ????????????????{ ????????????????????width = 0; ????????????????????height = 0; ????????????????} ????????????} ????????????catch?(Exception) ????????????{ ????????????????width = 0; ????????????????height = 0; ????????????} ????????} ? ????????public?static?void?ExecuteCommand(string?command,?out?string?output,?out?string?error) ????????{ ????????????try ????????????{ ????????????????//創建一個進程 ????????????????Process pc =?new?Process(); ????????????????pc.StartInfo.FileName = command; ????????????????pc.StartInfo.UseShellExecute =?false; ????????????????pc.StartInfo.RedirectStandardOutput =?true; ????????????????pc.StartInfo.RedirectStandardError =?true; ????????????????pc.StartInfo.CreateNoWindow =?true; ? ????????????????//啟動進程 ????????????????pc.Start(); ? ????????????????//準備讀出輸出流和錯誤流 ????????????????string?outputData =?string.Empty; ????????????????string?errorData =?string.Empty; ????????????????pc.BeginOutputReadLine(); ????????????????pc.BeginErrorReadLine(); ? ????????????????pc.OutputDataReceived += (ss, ee) => ????????????????{ ????????????????????outputData += ee.Data; ????????????????}; ? ????????????????pc.ErrorDataReceived += (ss, ee) => ????????????????{ ????????????????????errorData += ee.Data; ????????????????}; ? ????????????????//等待退出 ????????????????pc.WaitForExit(); ? ????????????????//關閉進程 ????????????????pc.Close(); ? ????????????????//返回流結果 ????????????????output = outputData; ????????????????error = errorData; ????????????} ????????????catch?(Exception) ????????????{ ????????????????output =?null; ????????????????error =?null; ????????????} ????????} ? ????} ? ????public?class?Util ????{ ????????public?static?string?GetRandomLetterAndNumberString(Random random,?int?length) ????????{ ????????????if?(length < 0) ????????????{ ????????????????throw?new?ArgumentOutOfRangeException("length"); ????????????} ????????????char[] pattern =?new?char[] {?'0',?'1',?'2',?'3',?'4',?'5',?'6',?'7',?'8',?'9', ????????'A',?'B',?'C',?'D',?'E',?'F',?'G',?'H',?'I',?'J',?'K',?'L',?'M',?'N',?'O',?'P', ????????'Q',?'R',?'S',?'T',?'U',?'V',?'W',?'X',?'Y',?'Z'?}; ????????????string?result =?""; ????????????int?n = pattern.Length; ????????????for?(int?i = 0; i < length; i++) ????????????{ ????????????????int?rnd = random.Next(0, n); ????????????????result += pattern[rnd]; ????????????} ????????????return?result; ????????} ????} ? ????class?Cmd ????{ ????????private?static?string?CmdPath =?@"C:\Windows\System32\cmd.exe"; ????????/// <summary> ????????/// 執行cmd命令 返回cmd窗口顯示的信息 ????????/// 多命令請使用批處理命令連接符: ????????/// <![CDATA[ ????????/// &:同時執行兩個命令 ????????/// |:將上一個命令的輸出,作為下一個命令的輸入 ????????/// &&:當&&前的命令成功時,才執行&&后的命令 ????????/// ||:當||前的命令失敗時,才執行||后的命令]]> ????????/// </summary> ????????/// <param name="cmd">執行的命令</param> ????????public?static?string?RunCmd(string?cmd) ????????{ ????????????cmd = cmd.Trim().TrimEnd('&') +?"&exit";//說明:不管命令是否成功均執行exit命令,否則當調用ReadToEnd()方法時,會處于假死狀態 ????????????using?(Process p =?new?Process()) ????????????{ ????????????????p.StartInfo.FileName = CmdPath; ????????????????p.StartInfo.UseShellExecute =?false;????????//是否使用操作系統shell啟動 ????????????????p.StartInfo.RedirectStandardInput =?true;???//接受來自調用程序的輸入信息 ????????????????p.StartInfo.RedirectStandardOutput =?true;??//由調用程序獲取輸出信息 ????????????????p.StartInfo.RedirectStandardError =?true;???//重定向標準錯誤輸出 ????????????????p.StartInfo.CreateNoWindow =?true;??????????//不顯示程序窗口 ????????????????p.Start();//啟動程序 ? ????????????????//向cmd窗口寫入命令 ????????????????p.StandardInput.WriteLine(cmd); ????????????????p.StandardInput.AutoFlush =?true; ? ????????????????//獲取cmd窗口的輸出信息 ????????????????string?output = p.StandardOutput.ReadToEnd(); ????????????????p.WaitForExit();//等待程序執行完退出進程 ????????????????p.Close(); ? ????????????????return?output; ????????????} ????????} ????} }<br data-filtered="filtered"><br data-filtered="filtered">我還是都寫到一塊了,大家記得分開! |
SController.cs? 這個是針對手機端單獨拎出來的,里面不需要什么內容
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using?System; using?System.Collections.Generic; using?System.Linq; using?System.Web; using?System.Web.Mvc; ? namespace?SignalR.Controllers { ????public?class?SController : Controller ????{ ????????// GET: S ????????public?ActionResult Index() ????????{ ????????????return?View(); ????????} ????} } |
根目錄新建一個ViewModels文件夾,里面新建FileModel.cs文件
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using?System; using?System.Collections.Generic; using?System.Linq; using?System.Web; ? namespace?SignalR.ViewModels { ????public?class?FileModel ????{ ????????/// <summary> ????????/// 1 : 圖片? 2:視頻 ????????/// </summary> ????????public?int?t {?get;?set; } ? ????????public?string?url {?get;?set; } ????} } |
RedisHelper.cs
| 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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | using?Microsoft.AspNet.SignalR.Messaging; using?StackExchange.Redis; using?System; using?System.Collections.Generic; using?System.IO; using?System.Linq; using?System.Net; using?System.Runtime.Serialization.Formatters.Binary; using?System.Threading.Tasks; using?System.Web; ? namespace?SignalR { ????public?class?RedisHelper ????{ ????????private?static?string?Constr =?"xxxx.cn:6379"; ? ????????private?static?object?_locker =?new?Object(); ????????private?static?ConnectionMultiplexer _instance =?null; ? ????????/// <summary> ????????/// 使用一個靜態屬性來返回已連接的實例,如下列中所示。這樣,一旦 ConnectionMultiplexer 斷開連接,便可以初始化新的連接實例。 ????????/// </summary> ????????public?static?ConnectionMultiplexer Instance ????????{ ????????????get ????????????{ ????????????????if?(Constr.Length == 0) ????????????????{ ????????????????????throw?new?Exception("連接字符串未設置!"); ????????????????} ????????????????if?(_instance ==?null) ????????????????{ ????????????????????lock?(_locker) ????????????????????{ ????????????????????????if?(_instance ==?null?|| !_instance.IsConnected) ????????????????????????{ ????????????????????????????_instance = ConnectionMultiplexer.Connect(Constr); ????????????????????????} ????????????????????} ????????????????} ????????????????//注冊如下事件 ????????????????_instance.ConnectionFailed += MuxerConnectionFailed; ????????????????_instance.ConnectionRestored += MuxerConnectionRestored; ????????????????_instance.ErrorMessage += MuxerErrorMessage; ????????????????_instance.ConfigurationChanged += MuxerConfigurationChanged; ????????????????_instance.HashSlotMoved += MuxerHashSlotMoved; ????????????????_instance.InternalError += MuxerInternalError; ????????????????return?_instance; ????????????} ????????} ? ????????static?RedisHelper() ????????{ ????????} ? ? ????????/// <summary> ????????/// ????????/// </summary> ????????/// <returns></returns> ????????public?static?IDatabase GetDatabase() ????????{ ????????????return?Instance.GetDatabase(); ????????} ? ????????/// <summary> ????????/// 這里的 MergeKey 用來拼接 Key 的前綴,具體不同的業務模塊使用不同的前綴。 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <returns></returns> ????????private?static?string?MergeKey(string?key) ????????{ ????????????return?"SignalR:"+ key; ????????????//return BaseSystemInfo.SystemCode + key; ????????} ? ????????/// <summary> ????????/// 根據key獲取緩存對象 ????????/// </summary> ????????/// <typeparam name="T"></typeparam> ????????/// <param name="key"></param> ????????/// <returns></returns> ????????public?static?T Get<T>(string?key) ????????{ ????????????key = MergeKey(key); ????????????return?Deserialize<T>(GetDatabase().StringGet(key)); ????????} ? ????????/// <summary> ????????/// 根據key獲取緩存對象 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <returns></returns> ????????public?static?object?Get(string?key) ????????{ ????????????key = MergeKey(key); ????????????return?Deserialize<object>(GetDatabase().StringGet(key)); ????????} ? ????????/// <summary> ????????/// 設置緩存 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <param name="value"></param> ????????/// <param name="expireMinutes"></param> ????????public?static?void?Set(string?key,?object?value,?int?expireMinutes = 0) ????????{ ????????????key = MergeKey(key); ????????????if?(expireMinutes > 0) ????????????{ ????????????????GetDatabase().StringSet(key, Serialize(value), TimeSpan.FromMinutes(expireMinutes)); ????????????} ????????????else ????????????{ ????????????????GetDatabase().StringSet(key, Serialize(value)); ????????????} ? ????????} ? ? ????????/// <summary> ????????/// 判斷在緩存中是否存在該key的緩存數據 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <returns></returns> ????????public?static?bool?Exists(string?key) ????????{ ????????????key = MergeKey(key); ????????????return?GetDatabase().KeyExists(key);?//可直接調用 ????????} ? ????????/// <summary> ????????/// 移除指定key的緩存 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <returns></returns> ????????public?static?bool?Remove(string?key) ????????{ ????????????key = MergeKey(key); ????????????return?GetDatabase().KeyDelete(key); ????????} ? ????????/// <summary> ????????/// 異步設置 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <param name="value"></param> ????????public?static?async Task SetAsync(string?key,?object?value) ????????{ ????????????key = MergeKey(key); ????????????await GetDatabase().StringSetAsync(key, Serialize(value)); ????????} ? ????????/// <summary> ????????/// 根據key獲取緩存對象 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <returns></returns> ????????public?static?async Task<object> GetAsync(string?key) ????????{ ????????????key = MergeKey(key); ????????????object?value = await GetDatabase().StringGetAsync(key); ????????????return?value; ????????} ? ????????/// <summary> ????????/// 實現遞增 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <returns></returns> ????????public?static?long?Increment(string?key) ????????{ ????????????key = MergeKey(key); ????????????//三種命令模式 ????????????//Sync,同步模式會直接阻塞調用者,但是顯然不會阻塞其他線程。 ????????????//Async,異步模式直接走的是Task模型。 ????????????//Fire - and - Forget,就是發送命令,然后完全不關心最終什么時候完成命令操作。 ????????????//即發即棄:通過配置 CommandFlags 來實現即發即棄功能,在該實例中該方法會立即返回,如果是string則返回null 如果是int則返回0.這個操作將會繼續在后臺運行,一個典型的用法頁面計數器的實現: ????????????return?GetDatabase().StringIncrement(key, flags: CommandFlags.FireAndForget); ????????} ? ????????/// <summary> ????????/// 實現遞減 ????????/// </summary> ????????/// <param name="key"></param> ????????/// <param name="value"></param> ????????/// <returns></returns> ????????public?static?long?Decrement(string?key,?string?value) ????????{ ????????????key = MergeKey(key); ????????????return?GetDatabase().HashDecrement(key, value, flags: CommandFlags.FireAndForget); ????????} ? ????????/// <summary> ????????/// 序列化對象 ????????/// </summary> ????????/// <param name="o"></param> ????????/// <returns></returns> ????????private?static?byte[] Serialize(object?o) ????????{ ????????????if?(o ==?null) ????????????{ ????????????????return?null; ????????????} ????????????BinaryFormatter binaryFormatter =?new?BinaryFormatter(); ????????????using?(MemoryStream memoryStream =?new?MemoryStream()) ????????????{ ????????????????binaryFormatter.Serialize(memoryStream, o); ????????????????byte[] objectDataAsStream = memoryStream.ToArray(); ????????????????return?objectDataAsStream; ????????????} ????????} ? ????????/// <summary> ????????/// 反序列化對象 ????????/// </summary> ????????/// <typeparam name="T"></typeparam> ????????/// <param name="stream"></param> ????????/// <returns></returns> ????????private?static?T Deserialize<T>(byte[] stream) ????????{ ????????????if?(stream ==?null) ????????????{ ????????????????return?default(T); ????????????} ????????????BinaryFormatter binaryFormatter =?new?BinaryFormatter(); ????????????using?(MemoryStream memoryStream =?new?MemoryStream(stream)) ????????????{ ????????????????T result = (T)binaryFormatter.Deserialize(memoryStream); ????????????????return?result; ????????????} ????????} ? ????????/// <summary> ????????/// 配置更改時 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?static?void?MuxerConfigurationChanged(object?sender, EndPointEventArgs e) ????????{ ????????????//LogHelper.SafeLogMessage("Configuration changed: " + e.EndPoint); ????????} ? ????????/// <summary> ????????/// 發生錯誤時 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?static?void?MuxerErrorMessage(object?sender, RedisErrorEventArgs e) ????????{ ????????????//LogHelper.SafeLogMessage("ErrorMessage: " + e.Message); ????????} ? ????????/// <summary> ????????/// 重新建立連接之前的錯誤 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?static?void?MuxerConnectionRestored(object?sender, ConnectionFailedEventArgs e) ????????{ ????????????//LogHelper.SafeLogMessage("ConnectionRestored: " + e.EndPoint); ????????} ? ????????/// <summary> ????????/// 連接失敗 , 如果重新連接成功你將不會收到這個通知 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?static?void?MuxerConnectionFailed(object?sender, ConnectionFailedEventArgs e) ????????{ ????????????//LogHelper.SafeLogMessage("重新連接:Endpoint failed: " + e.EndPoint + ", " + e.FailureType +(e.Exception == null ? "" : (", " + e.Exception.Message))); ????????} ? ????????/// <summary> ????????/// 更改集群 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?static?void?MuxerHashSlotMoved(object?sender, HashSlotMovedEventArgs e) ????????{ ????????????//LogHelper.SafeLogMessage("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint); ????????} ? ????????/// <summary> ????????/// redis類庫錯誤 ????????/// </summary> ????????/// <param name="sender"></param> ????????/// <param name="e"></param> ????????private?static?void?MuxerInternalError(object?sender, InternalErrorEventArgs e) ????????{ ????????????//LogHelper.SafeLogMessage("InternalError:Message" + e.Exception.Message); ????????} ? ????????//場景不一樣,選擇的模式便會不一樣,大家可以按照自己系統架構情況合理選擇長連接還是Lazy。 ????????//建立連接后,通過調用ConnectionMultiplexer.GetDatabase 方法返回對 Redis Cache 數據庫的引用。從 GetDatabase 方法返回的對象是一個輕量級直通對象,不需要進行存儲。 ? ????????/// <summary> ????????/// 使用的是Lazy,在真正需要連接時創建連接。 ????????/// 延遲加載技術 ????????/// 微軟azure中的配置 連接模板 ????????/// </summary> ????????//private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() => ????????//{ ????????//??? //var options = ConfigurationOptions.Parse(constr); ????????//??? options.ClientName = GetAppName(); // only known at runtime ????????//??? //options.AllowAdmin = true; ????????//??? //return ConnectionMultiplexer.Connect(options); ????????//??? ConnectionMultiplexer muxer = ConnectionMultiplexer.Connect(Coonstr); ????????//??? muxer.ConnectionFailed += MuxerConnectionFailed; ????????//??? muxer.ConnectionRestored += MuxerConnectionRestored; ????????//??? muxer.ErrorMessage += MuxerErrorMessage; ????????//??? muxer.ConfigurationChanged += MuxerConfigurationChanged; ????????//??? muxer.HashSlotMoved += MuxerHashSlotMoved; ????????//??? muxer.InternalError += MuxerInternalError; ????????//??? return muxer; ????????//}); ? ? ????????#region? 當作消息代理中間件使用 一般使用更專業的消息隊列來處理這種業務場景 ? ????????/// <summary> ????????/// 當作消息代理中間件使用 ????????/// 消息組建中,重要的概念便是生產者,消費者,消息中間件。 ????????/// </summary> ????????/// <param name="channel"></param> ????????/// <param name="message"></param> ????????/// <returns></returns> ????????public?static?long?Publish(string?channel,?string?message) ????????{ ????????????StackExchange.Redis.ISubscriber sub = Instance.GetSubscriber(); ????????????//return sub.Publish("messages", "hello"); ????????????return?sub.Publish(channel, message); ????????} ? ????????/// <summary> ????????/// 在消費者端得到該消息并輸出 ????????/// </summary> ????????/// <param name="channelFrom"></param> ????????/// <returns></returns> ????????public?static?void?Subscribe(string?channelFrom) ????????{ ????????????StackExchange.Redis.ISubscriber sub = Instance.GetSubscriber(); ????????????sub.Subscribe(channelFrom, (channel, message) => ????????????{ ????????????????Console.WriteLine((string)message); ????????????}); ????????} ? ????????#endregion ? ????????/// <summary> ????????/// GetServer方法會接收一個EndPoint類或者一個唯一標識一臺服務器的鍵值對 ????????/// 有時候需要為單個服務器指定特定的命令 ????????/// 使用IServer可以使用所有的shell命令,比如: ????????/// DateTime lastSave = server.LastSave(); ????????/// ClientInfo[] clients = server.ClientList(); ????????/// 如果報錯在連接字符串后加 ,allowAdmin=true; ????????/// </summary> ????????/// <returns></returns> ????????public?static?IServer GetServer(string?host,?int?port) ????????{ ????????????IServer server = Instance.GetServer(host, port); ????????????return?server; ????????} ? ????????/// <summary> ????????/// 獲取全部終結點 ????????/// </summary> ????????/// <returns></returns> ????????public?static?EndPoint[] GetEndPoints() ????????{ ????????????EndPoint[] endpoints = Instance.GetEndPoints(); ????????????return?endpoints; ????????} ????} } |
總體項目結構是這樣的
?
?
下期我將把前端代碼列出來,這個我只是為了實現功能,大神勿噴
?
轉自https://www.cnblogs.com/colyn/p/11976006.html
總結
以上是生活随笔為你收集整理的仿微信 即时聊天工具 - SignalR (一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 抽象代数基础教程 中文版下载(Rotma
- 下一篇: 创客匠人抖音小程序引流转化三步走