视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Asp.net Core中如何使用中间件来管理websocket
2020-11-27 22:34:54 责编:小采
文档

介绍

我喜欢.NET CORE 这个东西,其实不仅仅源于它性能很高,可以跨平台,还因为它的设计模式确实令人着迷。以前没.NET CORE 的时候,.NET用websocket必须跑在windows server 2012上,但我一般不会这么干,都把websocket架在nodejs的服务器上。这么分出来,麻烦肯定是麻烦的,而且js这东西,写复杂和几年后再看都是头疼的问题。那么,如果.NET CORE是以kestrel运行的,那么就不再需要考虑服务器的版本运行,任何一个地方都可以用websocket

ASP.NET Core SignalR是一个有用的库,可以简化Web应用程序中实时通信的管理。但是,我宁愿使用WebSockets,因为我想要更灵活,并且与任何WebSocket客户端兼容。

在Microsoft的文档中,我找到了一个很好的WebSockets工作示例。它仍然是管理连接,以便能够从一个连接向其他连接广播消息,这是SignalR开箱即用的功能。期望这个逻辑非常复杂,我想从Startup类中删除它。

背景

要阅读ASP.NET Core中的WebSockets支持,可以在此处查看。如果您想了解中间件以及如何在ASP.NET Core中编写它,请阅读此链接。

代码使用

首先,你必须添加 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的逻辑可以扩展,使您可以根据自己的喜好灵活地组织它。

总结

下载本文
显示全文
专题