最新 .NET Core 中 WebSocket的使用 在Asp.Net MVC 中 WebSocket的使用 .NET Core 中 SignalR的使用
ASP.NET MVC 中使用WebSocket 笔记
1.采用控制器的方法 这个只写建立链接的方法的核心方法
1.1 踩坑 网上都是直接 传个异步方法 直接接受链接 自己尝试了好多次链接是打开的,到时获取不到链接里面的内容!! (如果是我理解有问题的话,欢迎讨论,毕竟这个问题卡了我好久!)
1.2 自己建立链接的使用截图 方法
1 /// <summary> 2 /// WebSocket链接 demo地址 ws://Localhost:8080/Get 3 /// </summary> 4 [HttpGet] 5 public void Get() 6 { 7 if (System.Web.HttpContext.Current.IsWebSocketRequest) 8 { 9 //System.Web.HttpContext.Current.AcceptWebSocketRequest(AnalysisOptionChat); 10 System.Web.HttpContext.Current.AcceptWebSocketRequest(async (context) => await AnalysisOptionChat(context)); 11 } 12 }
1.3 判断是WebSocket 链接后 处理消息
1 /// <summary> 2 /// WebSocket链接操作 3 /// </summary> 4 /// <param name="context"></param> 5 /// <returns></returns> 6 private async Task AnalysisOptionChat(AspNetWebSocketContext context) 7 { 8 var webSocket = context.WebSocket; 9 #region 测试demo 10 try 11 { 12 ConsoleWrite.ConsoleInfo($"Websocket client add--> 自己需要实现 保存链接"); 13 WebSocketReceiveResult clientData = null; 14 do 15 { 16 var buffer = new ArraySegment<byte>(new byte[1024 * 1000]); 17 //if (clientData.MessageType == WebSocketMessageType.Text && !clientData.CloseStatus.HasValue) 18 if (webSocket.State == WebSocketState.Open && clientData.MessageType == WebSocketMessageType.Text) 19 { 20 string msgString = Encoding.UTF8.GetString(buffer.Array, 0, clientData.Count); 21 var sendData = string.Empty; 22 if ("rub".Equals(msgString))//心跳 23 { 24 25 } 26 else 27 { 28 int size = clientData.Count; 29 30 #region 通知 31 #endregion 32 33 #region DB持久化 操作 34 #endregion 35 } 36 } 37 } 38 //while (!clientData.CloseStatus.HasValue); 39 while (webSocket.State == WebSocketState.Open); 40 41 var state = webSocket.State; 42 ConsoleWrite.ConsoleWarning($"Websocket client closed1-->{state.ToString()}"); 43 ConsoleWrite.ConsoleWarning($"Websocket client closed2-->{clientData.CloseStatus.Value}"); 44 //关闭事件 45 await webSocket.CloseAsync(clientData.CloseStatus.Value, clientData.CloseStatusDescription, CancellationToken.None); 46 } 47 catch (Exception ex) 48 { 49 Console.WriteLine(ex.Message); 50 } 51 52 #endregion 53 }
1.4 在Asp.Net MVC 中使用可能会遇到 跨域的问题 一下是解决方案
1 <system.webServer> 2 <!--全局跨域--> 3 <httpProtocol> 4 <customHeaders> 5 <add name="Access-Control-Allow-Origin" value="*" /> 6 <!--<add name="Access-Control-Allow-Headers" value="*" /> 7 <add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />--> 8 </customHeaders> 9 </httpProtocol> 10 <system.webServer>
.NET Core 中使用WebSocket 笔记
1.在WepApi中使用法 这个只写建立链接的方法的核心方法
1.首先需要在Startup.cs 里面的 ConfigureServices里面配置跨域 如果不牵扯跨域的话 可以忽略 1,2 (里面的跨域) 跨域详细配置如下
1 services.AddCors(options => 2 { 3 options.AddPolicy("WebSocketCors", builder => 4 { 5 builder.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials(); 6 }); 7 });
2.然后在 Configure方法 里面加入管道
1 app.UseWebSockets(new Microsoft.AspNetCore.Builder.WebSocketOptions 2 { 3 //保持活动间隔 4 KeepAliveInterval = TimeSpan.FromMinutes(5), 5 }); 6 7 // 注意这个是重点!!!! 8 app.UseMiddleware<WebsocketHandlerMiddleware>(); 9 10 //自定义跨域规则 11 app.UseCors("WebSocketCors");
详细截图如下
3. 请求中间件 WebsocketHandlerMiddleware.cs 详细介绍如下
1 public class WebsocketHandlerMiddleware 2 { 3 private readonly RequestDelegate _next; 4 5 public WebsocketHandlerMiddleware(RequestDelegate next) 6 { 7 _next = next; 8 } 9 10 public async Task Invoke(HttpContext context) 11 { 12 if (context.Request.Path == "/ws") 13 { 14 //客户端与服务器成功建立连接后,服务器会循环异步接收客户端发送的消息,收到消息后就会执行Handle(WebsocketClient websocketClient)中的do{}while;直到客户端断开连接 15 //不同的客户端向服务器发送消息后台执行do{}while;时,websocketClient实参是不同的,它与客户端一一对应 16 //同一个客户端向服务器多次发送消息后台执行do{}while;时,websocketClient实参是相同的 17 if (context.WebSockets.IsWebSocketRequest) 18 { 19 var webSocket = await context.WebSockets.AcceptWebSocketAsync(); 20 21 } 22 else 23 { 24 context.Response.StatusCode = 404; 25 } 26 } 27 else 28 { 29 await _next(context); 30 } 31 } 32 }
里面处理 WebSocket 请求消息的内容 和 1.3 中方法是一样的 我这里就不重复了 两者的区别都只是 获取方式不一样而已。
.NET Core 中使用SignalR笔记
1.在WepApi中使用法 这个只写建立链接的方法的核心方法 以及相关方法介绍
所使用的Nuget 包 如下:
1.1 首先需要在Startup.cs 里面的 ConfigureServices方法 注入且配置跨域.
1 #region SignalR 2 3 services.AddCors(options => 4 { 5 options.AddPolicy("SignalRCors", builder => 6 { 7 builder.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials(); 8 }); 9 }); 10 11 services.AddSignalR().AddHubOptions<SignalrHub>(options => 12 { 13 options.EnableDetailedErrors = true; 14 }) 15 // 支持MessagePack 16 .AddMessagePackProtocol(); 17 18 #endregion
同时还需要 注入加入声明周期 管控
//特殊的
services.AddSingleton<SignalrHub>();
1.2 在Configure方法里面 的相关使用
1 //自定义跨域规则 2 app.UseCors("SignalRCors"); 3 4 //需要在 Map里面 指定路由 5 //app.UseHttpsRedirection(); 6 app.UseEndpoints(endpoints => 7 { 8 endpoints.MapControllers(); 9 //可以设置SignalR相关参数,这里设置地址 10 endpoints.MapHub<SignalrHub>("hubs/signalr", options => 11 { 12 //配置 几种方式 13 options.Transports = HttpTransportType.WebSockets | HttpTransportType.LongPolling; 14 }); 15 });
1.3 SignalrHub.cs 集线器类的使用介绍 里面有许多用不上的 自己按照需求自己过滤 主要就是 OnConnectedAsync() 建立链接 OnDisconnectedAsync()断开链接 方法的使用!
1 //创建SingalR中心跨域 2 [EnableCors("SignalRCors")] 3 public class SignalrHub : Hub 4 { 5 //ReceiveMessage 为客户端监听的 方法名称 6 private static string clientSendMethodName = "ReceiveMessage"; 7 8 //Redis 存储信息Key 9 private static string signalrRedisKey = "SignalRConnections"; 10 11 public SignalrHub() 12 { 13 14 } 15 16 17 #region Send 18 19 /// <summary> 20 /// 发送消息-指定用户集合发送消息 21 /// </summary> 22 /// <param name="userIds">通知用户集合</param> 23 /// <param name="sendObject">通知OBject</param> 24 /// <returns></returns> 25 public async Task SendUserMessage(List<int> userIds,object sendObject) 26 { 27 //从redis 获取 userIds 对应的 ConnectionId 进行推送 28 var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue); 29 if(connectionUserList.Count()<=0) throw new Exception("无连接用户,无法进行消息推送。"); 30 var sentUserClient = connectionUserList.Where(x => userIds.Contains(x.UserId)).ToList(); 31 var sendMeaaageToJson = JsonHelper.ObjectToJsonCamelCase(sendObject); 32 33 var connectionIds = sentUserClient.Select(y => y.ConnectionId); 34 35 //全部 36 //await Clients.All.SendAsync(clientSendMethodName, new { data = sendMeaaageToJson }); 37 38 //指定 39 await Clients.Clients(connectionIds).SendAsync(clientSendMethodName, new { data = sendMeaaageToJson }); 40 } 41 42 43 //发送消息--发送给所有连接的客户端 44 public async Task SendMessage(string msg) 45 { 46 //await Clients.All.SendAsync(clientSendMethodName, msg); 47 Console.WriteLine($"保持心跳链接-->接收参数:{msg}"); 48 } 49 50 51 52 /// <summary> 53 /// 除 connectionId 之外的所有发送message 54 /// </summary> 55 /// <param name="connectionId"></param> 56 /// <param name="message"></param> 57 /// <returns></returns> 58 public Task SendAllExceptMe(string connectionId, string message) 59 { 60 61 return Clients.AllExcept(connectionId).SendAsync(clientSendMethodName, $"{Context.ConnectionId}: {message}"); 62 } 63 64 #endregion 65 66 67 68 #region Group分组的话 依据职位 或者 角色来业务循环获取 69 70 71 72 #endregion 73 74 75 #region overrides 76 77 /// <summary> 78 /// 当新的客户端连接建立时执行的操作 79 /// </summary> 80 /// <returns></returns> 81 public override async Task OnConnectedAsync() 82 { 83 //建立者用户Id 84 var clientUserId = AuthHelper.GetUserId(Context.User); 85 //建立者用户Name 86 var clientUserName = Context.User.Identity.Name; 87 //建立ConnectionId 88 var connectionId = Context.ConnectionId; 89 90 if (clientUserId <= 0 || string.IsNullOrWhiteSpace(clientUserName)) 91 { 92 throw new Exception("建立连接异常,无法获取用户信息。"); 93 } 94 95 Console.WriteLine($"OnConnectedAsync建立链接----userId:{clientUserId},userName:{clientUserName},connectionId:{ Context.ConnectionId}"); 96 97 var userConnections = new List<UserConnection>(); 98 userConnections.Add(new UserConnection() { UserId = clientUserId, UserName = clientUserName, ConnectionId = connectionId, CreateTime = DateTime.Now }); 99 100 //redis存储连接用户信息 101 RedisService.SetLPushValue(signalrRedisKey, false, userConnections); 102 103 //获取所有用户的链接信息 104 //var aaa = RedisService.GetLPushData<UserConnection>(signalrRedisKey,0,10000000); 105 106 await base.OnConnectedAsync(); 107 } 108 109 110 /// <summary> 111 /// 当客户端断开连接时执行的操作 112 /// </summary> 113 /// <param name="exception"></param> 114 /// <returns></returns> 115 public override async Task OnDisconnectedAsync(Exception exception) 116 { 117 //建立者用户Id 118 var clientUserId = AuthHelper.GetUserId(Context.User); 119 //建立者用户Name 120 var clientUserName = Context.User.Identity.Name; 121 //建立ConnectionId 122 var connectionId = Context.ConnectionId; 123 //NLogHelper.ActionWarn(); 124 125 Console.WriteLine($"OnDisconnectedAsync断开链接----userId:{clientUserId},clientUserName:{clientUserName},connectionId:{ connectionId}"); 126 127 var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue); 128 129 //在redis里面依据 Value 移除 130 var removeItem = connectionUserList.Where(x => x.ConnectionId == connectionId).FirstOrDefault(); 131 if (removeItem != null) 132 { 133 var Drow = await RedisService.LRemAsync(signalrRedisKey, removeItem); 134 //var connectionUserLists = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue); 135 } 136 await base.OnDisconnectedAsync(exception); 137 } 138 139 protected override void Dispose(bool disposing) 140 { 141 base.Dispose(disposing); 142 } 143 144 #endregion 145 146 147 #region ClearOverTime 清除超时的垃圾数据 148 149 public async Task ClearOverTimeUserRedis(int houre=24) 150 { 151 var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue); 152 var thisTime = DateTime.Now; 153 var delConnection = connectionUserList.Where(x=>(thisTime-x.CreateTime).Duration().TotalHours >= houre).ToList(); 154 if (delConnection != null) 155 { 156 foreach (var delItem in delConnection) 157 { 158 var Drow = await RedisService.LRemAsync(signalrRedisKey, delItem); 159 //var connectionUserLists = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue); 160 } 161 } 162 } 163 164 #endregion 165 }
1.4 SignalrHub.cs 里面所用到的用户链接 UserConnection.cs 类
1 [Serializable] 2 public class UserConnection 3 { 4 /// <summary> 5 /// 用户Id 6 /// </summary> 7 public int UserId { set; get; } 8 9 /// <summary> 10 /// 用户名称 11 /// </summary> 12 public string UserName { set; get; } 13 14 /// <summary> 15 /// 连接Id 16 /// </summary> 17 public string ConnectionId { set; get; } 18 19 /// <summary> 20 /// 创建时间 21 /// </summary> 22 public DateTime CreateTime { set; get; } 23 }
作者声明:
1.文章转载请注明出处 !!!
2.文章 如有不正确之处欢迎大家讨论,交流, 如果感觉写的还行,或者帮助了您,请点个赞哈,再次谢过~