介绍
ASP.NET Core SignalR 是一个有用的库, 可以简化 web 应用程序中实时通信的管理. 但是, 我宁愿使用 WebSockets, 因为我想要更灵活, 并且与任何 WebSocket 客户端兼容.
在 Microsoft 的文档中, 我找到了一个很好的 WebSockets 工作示例. 它仍然是管理连接, 以便能够从一个连接向其他连接广播消息, 这是 SignalR 开箱即用的功能. 期望这个逻辑非常复杂, 我想从 Startup 类中删除它.
背景
要阅读 ASP.NET Core 中的 WebSockets 支持, 可以在此处查看 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-2.1 . 如果您想了解中间件以及如何在 ASP.NET Core 中编写它, 请阅读此链接 https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1 .
代码使用
首先, 你必须添加 Microsoft.AspNetCore.WebSockets 包到你的项目.
现在, 您可以创建一个扩展方法和类来管理 WebSockets:
- public static class WebSocketExtensions
- { public static IApplicationBuilder UseCustomWebSocketManager(this IApplicationBuilder app)
- {
- return app.UseMiddleware<CustomWebSocketManager>();
- }
- }
- public class CustomWebSocketManager
- {
- private readonly RequestDelegate _next;
- public CustomWebSocketManager(RequestDelegate next)
- {
- _next = next;
- }
- public async Task Invoke(HttpContext context, ICustomWebSocketFactory wsFactory, ICustomWebSocketMessageHandler wsmHandler)
- {
- if (context.Request.Path == "/ws")
- {
- if (context.WebSockets.IsWebSocketRequest)
- {
- string username = context.Request.Query["u"];
- if (!string.IsNullOrEmpty(username))
- {
- WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
- CustomWebSocket userWebSocket = new CustomWebSocket()
- {
- WebSocket = webSocket,
- Username = username
- };
- wsFactory.Add(userWebSocket);
- await wsmHandler.SendInitialMessages(userWebSocket);
- await Listen(context, userWebSocket, wsFactory, wsmHandler);
- }
- }
- else
- {
- context.Response.StatusCode = 400;
- }
- }
- await _next(context);
- }
- private async Task Listen(HttpContext context, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory, ICustomWebSocketMessageHandler wsmHandler)
- {
- WebSocket webSocket = userWebSocket.WebSocket;
- var buffer = new byte[1024 * 4];
- WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
- while (!result.CloseStatus.HasValue)
- {
- await wsmHandler.HandleMessage(result, buffer, userWebSocket, wsFactory);
- buffer = new byte[1024 * 4];
- result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
- }
- wsFactory.Remove(userWebSocket.Username);
- await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
- }
- }
在这种情况下, WebSockets 请求在 URL 中始终包含 "/ ws". 查询字符串包含用于将 WebSocket 与登录用户相关联的用户名的参数 u.
CustomWebSocket 是一个包含 WebSocket 和用户名的类:
- public class CustomWebSocket
- {
- public WebSocket WebSocket { get; set; }
- public string Username { get; set; }
- }
我也创建了自定义 WebSocket 消息:
- class CustomWebSocketMessage
- {
- public string Text { get; set; }
- public DateTime MessagDateTime { get; set; }
- public string Username { get; set; }
- public WSMessageType Type { get; set; }
- }
其中 Type 是您可能拥有的不同类型消息的枚举.
在 Startup 类中, 您必须注册以下服务:
- services.AddSingleton<ICustomWebSocketFactory, CustomWebSocketFactory>();
- services.AddSingleton<ICustomWebSocketMessageHandler, CustomWebSocketMessageHandler>();
CustomWebSocketFactory 负责收集连接的 WebSockets 列表:
- public interface ICustomWebSocketFactory
- {
- void Add(CustomWebSocket uws);
- void Remove(string username);
- List<CustomWebSocket> All();
- List<CustomWebSocket> Others(CustomWebSocket client);
- CustomWebSocket Client(string username);
- }
- public class CustomWebSocketFactory : ICustomWebSocketFactory
- {
- List<CustomWebSocket> List;
- public CustomWebSocketFactory()
- {
- List = new List<CustomWebSocket>();
- }
- public void Add(CustomWebSocket uws)
- {
- List.Add(uws);
- }
- //when disconnect
- public void Remove(string username)
- {
- List.Remove(Client(username));
- }
- public List<CustomWebSocket> All()
- {
- return List;
- }
- public List<CustomWebSocket> Others(CustomWebSocket client)
- {
- return List.Where(c => c.Username != client.Username).ToList();
- }
- public CustomWebSocket Client(string username)
- {
- return List.First(c=>c.Username == username);
- }
- }
CustomWebSocketMessageHandler 包含有关消息的逻辑 (即在连接时需要发送任何消息以及如何对传入消息作出反应)
- public interface ICustomWebSocketMessageHandler
- {
- Task SendInitialMessages(CustomWebSocket userWebSocket);
- Task HandleMessage(WebSocketReceiveResult result, byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);
- Task BroadcastOthers(byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);
- Task BroadcastAll(byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory);
- }
- public class CustomWebSocketMessageHandler : ICustomWebSocketMessageHandler
- {
- public async Task SendInitialMessages(CustomWebSocket userWebSocket)
- {
- WebSocket webSocket = userWebSocket.WebSocket;
- var msg = new CustomWebSocketMessage
- {
- MessagDateTime = DateTime.Now,
- Type = WSMessageType.anyType,
- Text = anyText,
- Username = "system"
- };
- string serialisedMessage = JsonConvert.SerializeObject(msg);
- byte[] bytes = Encoding.ASCII.GetBytes(serialisedMessage);
- await webSocket.SendAsync(new ArraySegment<byte>(bytes, 0, bytes.Length), WebSocketMessageType.Text, true, CancellationToken.None);
- }
- public async Task HandleMessage(WebSocketReceiveResult result, byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)
- {
- string msg = Encoding.ASCII.GetString(buffer);
- try
- {
- var message = JsonConvert.DeserializeObject<CustomWebSocketMessage>(msg);
- if (message.Type == WSMessageType.anyType)
- {
- await BroadcastOthers(buffer, userWebSocket, wsFactory);
- }
- }
- catch (Exception e)
- {
- await userWebSocket.WebSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);
- }
- }
- public async Task BroadcastOthers(byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)
- {
- var others = wsFactory.Others(userWebSocket);
- foreach (var uws in others)
- {
- await uws.WebSocket.SendAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None);
- }
- }
- public async Task BroadcastAll(byte[] buffer, CustomWebSocket userWebSocket, ICustomWebSocketFactory wsFactory)
- {
- var all = wsFactory.All();
- foreach (var uws in all)
- {
- await uws.WebSocket.SendAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None);
- }
- }
- }
最后, 在 Configure 方法的 Startup 类中添加以下内容:
- var webSocketOptions = new WebSocketOptions()
- {
- KeepAliveInterval = TimeSpan.FromSeconds(120),
- ReceiveBufferSize = 4 * 1024
- };
- app.UseWebSockets(webSocketOptions);
- app.UseCustomWebSocketManager();
通过这种方式, Starup 类保持干净, 管理 WebSockets 的逻辑可以扩展, 使您可以根据自己的喜好灵活地组织它. 喜欢这篇文章就转载吧.
来源: https://www.cnblogs.com/bruceday/p/9600770.html