当前位置: 首页 > 编程笔记 >

Asp.net Core中如何使用中间件来管理websocket

弓玉书
2023-03-14
本文向大家介绍Asp.net Core中如何使用中间件来管理websocket,包括了Asp.net Core中如何使用中间件来管理websocket的使用技巧和注意事项,需要的朋友参考一下

介绍

我喜欢.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的逻辑可以扩展,使您可以根据自己的喜好灵活地组织它。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对小牛知识库的支持。

 类似资料:
  • 在 YOG2 中,中间件管理 http 本身是一个插件,而各种中间件也是由插件组成,因此我们可将中间件插件理解为一类特殊的插件。 中间件管理插件的功能是根据用户指定的中间件加载顺序调用中间件插件。 中间件配置 中间件配置位于 /conf/plugins/http.js 中 module.exports.http = { middleware: [ 'favicon',

  • Flyway 是一个数据迁移工具。 简介 什么是 Flyway Flyway 是一个开源的数据库迁移工具。 为什么要使用数据迁移 为了说明数据迁移的作用,我们来举一个示例: (1)假设,有一个叫做 Shiny 的项目,它的架构是一个叫做 Shiny Soft 的 App 连接叫做 Shiny DB 的数据库。 (2)对于大多数项目而言,最简单的持续集成场景如下所示: 这意味着,我们不仅仅要处理一份

  • 问题内容: 关于一个主题,人们可以使用新语言Swift轻松查看如何在Web视图中管理Cookie呢?如果您在Internet上签到,则在需要实现此功能时不会发现任何有趣的东西。甚至苹果公司的文件也很差。 有人知道如何在Swift中处理这些过程吗?这是我在Obj-C中找到的: 查看存储的Cookie 删除存储的Cookie 如果我们能给出一次答案的话,对每个人都很好!干杯! 问题答案: 试试这个代码

  • 问题内容: 删除了对ModelAdmin方法上allow_tags属性的支持。 问题答案: 刚找到答案,使用功能。 在旧代码中,您可以使用: 在新代码中,应使用:

  • 我正在尝试将这个示例项目构建为Azure函数,它需要一些包https://github.com/OfficeDev/O365-Nodejs-Microsoft-Graph-App-only 我可以看到,我可以在NodeJS Azure函数中使用数据包管理https://azure.microsoft.com/en-us/documentation/articles/functions-refere

  • 问题内容: 一个最近的问题在这里SO让我思考。 在我尝试过的大多数Linux发行版中,某些Perl模块可以通过软件包管理器获得。其他人当然不会。在相当长的一段时间里,每当我需要安装一些CPAN模块来查找某个软件包是否可用并在安装该软件包时都将使用我的软件包管理器。 明显的好处是,只要有新版本的软件包可用,您就可以更新模块。 但是,当模块无法以预打包的形式提供并且该模块存在依赖项时,您会遇到麻烦。每