最新 .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         }
View Code

 

 

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 }
View Code

 

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>
View Code

 

.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             });
View Code

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");
View Code

详细截图如下

 

 

 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     }
View Code

里面处理 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
View Code

同时还需要 注入加入声明周期 管控 

//特殊的
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             });
View Code

 

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     }
View Code

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     }
View Code

 

作者声明:

1.文章转载请注明出处 !!! 

2.文章 如有不正确之处欢迎大家讨论,交流, 如果感觉写的还行,或者帮助了您,请点个赞哈,再次谢过~

 

posted @ 2021-08-31 20:03  雨太阳  阅读(1235)  评论(0编辑  收藏  举报