• 在Asp.net Core中使用中间件来管理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的逻辑可以扩展,使您可以根据自己的喜好灵活地组织它。喜欢这篇文章就转载吧。在Asp.net Core中使用中间件来管理websocket

  • 相关阅读:
    HTML 文本格式化实例
    HTML 文本格式化实例--预格式文本+“计算机输出”标签
    HTML 文本格式化实例--文本格式化
    如何用通俗易懂的语言解释脚本(script)是什么?
    1. HTML 基础标签
    java 和 python的一些对比
    XML fragments parsed from previous mappers does not contain value for com.miniprogram.meirong.comment.dao.CommentMapper.Base_Column_List
    The request was rejected because the URL was not normalized.
    fastjson的简单使用
    微信小程序的分页
  • 原文地址:https://www.cnblogs.com/bruceday/p/9600770.html
Copyright © 2020-2023  润新知