如何在 asp.net core 中使用 websockets

     2023-02-26     238

关键词:

【中文标题】如何在 asp.net core 中使用 websockets【英文标题】:How to use websockets in asp.net core 【发布时间】:2020-10-26 15:01:05 【问题描述】:

我正在尝试开发一个游戏,我将记分牌存储在一个文本文件中,该文件存储在服务器上(当前位于本地主机上)。我正在使用 http get 和 post 调用来与服务器通信并获取和发送我想要的数据。现在我想实现 websockets 以便将通知从服务器发送到 c# 客户端。通知只会在控制台上为用户显示一条消息,例如在 mu 情况下,我想在每次将用户添加到记分牌时,每次调用 UpdateScoreBoard 方法时向用户显示一条消息。根据我在网上找到的教程,我设法构建了以下代码,谁能让我更清楚我将如何为服务器构建 websocket 以及如何在客户端初始化 websocket?谢谢

Startup.cs(服务器)

        public void Configure(IApplicationBuilder app, IHostEnvironment env)
        
          //deleted code

            var webSocketOptions = new WebSocketOptions()
            
                KeepAliveInterval = TimeSpan.FromSeconds(120),
                ReceiveBufferSize = 4 * 1024
            ;


            app.UseWebSockets(webSocketOptions);
        
            app.Use(async (context, next) =>
            
                if (context.Request.Path == "/ws")
                
                    if (context.WebSockets.IsWebSocketRequest)
                    
                        WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
                        await Echo(context, webSocket);
                    
                    else
                    
                        context.Response.StatusCode = 400;
                    
                
                else
                
                    await next();
                

            );
        

        private async Task Echo(HttpContext context, WebSocket webSocket)
        
            var buffer = new byte[1024 * 4];
            WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            while (!result.CloseStatus.HasValue)
            
                await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, result.Count), result.MessageType, result.EndOfMessage, CancellationToken.None);

                result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            
            await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
        

HttpClass.cs (Client) - 我在这里调用 http post 请求

public async override Task<List<Scoreboard>> UpdateScoreBoards(string username, int attempts, int seconds, DateTime date)
            
                HttpResponseMessage response = null;
                //Creating a new instance of object Scoreboard
                //deleted code

                var url = "http://localhost:5000/api/Scoreboard";

                var socket_url = new Uri("ws://localhost:5000"); 
                var exitEvent = new ManualResetEvent(false);
                using (var client = new WebsocketClient(socket_url))
                
                    client.ReconnectTimeout = TimeSpan.FromSeconds(30);
                    client.ReconnectionHappened.Subscribe(info =>
                        Log.Information($"Reconnection happened, type: info.Type"));

                    client.MessageReceived.Subscribe(msg => Log.Information($"Message received: msg"));
                    await client.Start();

                    await Task.Run(() => client.Send("test"));

                    exitEvent.WaitOne();
                

// deleted code
            

【问题讨论】:

【参考方案1】:

谁能让我更清楚我将如何为服务器构建 websocket 以及如何在客户端初始化 websocket?

如您引用的示例所示,在 ASP.NET Core 中使用 WebSocket,我们可以在 Configure 方法中添加 WebSockets middleware,然后添加/配置请求委托以检查和处理传入的 WebSocket 请求。

并且在使用AcceptWebSocketAsync()方法将请求转换为WebSocket连接后,我们可以使用返回的WebSocket对象来发送和接收消息。

Echo 方法中,我们还可以执行自定义代码逻辑,根据收到的消息生成和发送回复消息/通知。

//received message
var mes = Encoding.UTF8.GetString(buffer, 0, result.Count);

//code logic here
//...

//create reply message
var reply_mes = $"You sent mes.";

byte[] reply_mes_buffer = Encoding.UTF8.GetBytes(reply_mes);

await webSocket.SendAsync(new ArraySegment<byte>(reply_mes_buffer, 0, reply_mes.Length), result.MessageType, result.EndOfMessage, CancellationToken.None);

此外,ASP.NET Core SignalR 是一个开源库,可简化实时通信功能的实现。而且它确实支持 WebSockets 传输,我们可以轻松实现向所有连接的客户端或连接的客户端的指定子集推送消息/通知。

有关 ASP.NET Core SignalR 的更多信息,您可以查看此文档:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-3.1

【讨论】:

【参考方案2】:

您唯一需要在Startup 中添加UseWebsockets 中间件。 然后,您可以定义自己的中间件并过滤连接,如果它们是 websocket 类型,如下所示:

启动

public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 
            app.UseWebSockets();
            app.UseMiddleware<SocketWare>();
        

中间件

public class SocketWare 
        private RequestDelegate next;
        public SocketWare(RequestDelegate _next) 
            this.next = _next;
        
        public async Task Invoke(HttpContext context) 
            if (!context.WebSockets.IsWebSocketRequest) 
                return;
            
            var socket=await context.WebSockets.AcceptWebSocketAsync();
            await RunAsync(socket);
        
        private async Task RunAsync(WebSocket socket) 
            try 
                var client = new ChatClient(socket);
                await client.RunAsync();
             catch (Exception ex) 

                throw;
            
            
        
        

    

在我的中间件中,我更喜欢将我的业务逻辑保留在一个单独的类中,该类将Websocket 注入其中,如下所示:

客户

public class ChatClient

   private Task writeTask;
   private Task readTask;
   private WebSocket socket;
   private CancellationTokenSource cts=new CancellationTokenSource();
   ChatClient(WebSocket socket)
   
       this.socket=socket;
   
   public async Task RunAsync()
   
      this.readTask=Task.Run(async ()=>await ReadLoopAsync(cts.Token),cts.Token);
      this.writeTask=Task.Run(async()=>await WriteLoopAsync(cts.Token),cts.Token);
      await Task.WhenAny(this.readTask,this.writeTask);
   
   public async Task WriteLoopAsync()
   
       Memory<byte> buffer=ArrayPool<byte>.Shared.Rent(1024);
       try 
           while (true) 
              var result= await this.socket.ReceiveAsync(buffer,....);
              var usefulBuffer=buffer.Slice(0,result.Count).ToArray();
              var raw=Encoding.Utf8.GetString(usefulBuffer);
              //deserialize it to whatever you need
              //handle message as you please (store it somwhere whatever)
            
         catch (Exception ex) 

               //socket error handling
               //break loop or continue with go to
        
   
   public async Task ReadLoopAsync()
   
          try 
            while (true) 
              
                var data = await this.[someMessageProvider].GetMessageAsync() //read below !!!
                var bytes = Encoding.UTF8.GetBytes(data);
                //send the message on the websocket
                await this.socket.SendAsync(data, WebSocketMessageType.Text, true, CancellationToken.None);
            
         catch (Exception ex) 

            //do incorrect message/socket disconnect logic
        
   

现在关于生成消息和使用它们。在您的情况下,您可以将您的生产者定义为一些 Controller 路由,如下所示。您将点击一个路由,生成一条消息并将其发布到某个消息代理。我会使用消息队列(RabbitMQ)甚至Redis Pub/Sub 作为消息总线。 您将从route(s) 发布消息,然后在WebSocketClientReadLoopAsync 方法中使用它们(见上图)。

生成消息

public UpdateController:Controller

   private IConnection
   [HttpPost]
   [someroute]
   public void UpdateScoreboard(string someMessage)
   
       this.connection.Publish("someChannel",someMessage);
   
   [HttpPost]
   [someotherroute]
   public void DeletePlayer(string someOtherMessage)
   
       this.connection.Publish("someChannel",someMessage);
   

Redis pub/sub 检查 redis pub/sub here 还要检查我的存储库 在我所在的 github here 使用你需要的东西(websockets、redis、pub sub)

RabbitMq 作为消息总线的另一个选项是使用 RabbitMQ ,以获取有关 C# 的更多信息 API here

记忆中

您也可以避免使用第三方并使用一些内存数据 像BlockingCollection 这样的结构。你可以将它注入 Singleton 在您的Controller(s) 和您的套接字中提供服务 Middleware(s)

【讨论】:

我们如何在 ASP.Net Core 中使用 HttpClient?

】我们如何在ASP.NetCore中使用HttpClient?【英文标题】:HowcanweuseHttpClientinASP.NetCore?【发布时间】:2019-09-1422:32:01【问题描述】:我正在编写ASP.NetMVCCore2.2Web应用程序。我需要使用HTTP或HTTPS从另一个Web服务器获取数据。我该怎么做... 查看详情

如何在 asp.net core 2.1 中使用 net.tcp 服务

】如何在asp.netcore2.1中使用net.tcp服务【英文标题】:HowdoIconsumeanet.tcpserviceinasp.netcore2.1【发布时间】:2019-04-0212:26:44【问题描述】:我正在构建一个asp.netcore2.1Web应用程序,我需要调用一个旧的net.tcp端点来获取有关预订的详细信... 查看详情

如何在 ASP.NET Core Web API 中发布对象列表

】如何在ASP.NETCoreWebAPI中发布对象列表【英文标题】:HowtopostlistofobjectinASP.NETCoreWebAPI【发布时间】:2019-08-0602:40:39【问题描述】:我看过一些教程,其中使用POST方法发送json对象。[HttpPost]publicasyncTask<IActionResult>createEntity([From... 查看详情

如何使用 Microsoft 身份平台身份验证在 ASP.NET Core Web 应用程序中获取 JWT 令牌?

】如何使用Microsoft身份平台身份验证在ASP.NETCoreWeb应用程序中获取JWT令牌?【英文标题】:HowdoIgetholdofaJWTtokeninanASP.NETCoreWebAppusingMicrosoftidentityplatformauthentication?【发布时间】:2021-12-2404:30:55【问题描述】:我已经使用VisualStudio2022... 查看详情

如何在 Web API Asp.NET Core 5 中持久化常用数据?

】如何在WebAPIAsp.NETCore5中持久化常用数据?【英文标题】:HowtopersistfrequentlyuseddatainWebAPIAsp.NETCore5?【发布时间】:2021-07-0404:16:20【问题描述】:我正在使用ASP.NETCore5开发WebAPI服务。如果用户已登录,我将使用JWT令牌对每个请求进... 查看详情

如何在 Asp Net Core Web App(不是 WebAPI)中存储令牌

】如何在AspNetCoreWebApp(不是WebAPI)中存储令牌【英文标题】:HowtostoretokeninAspNetCoreWebApp(notWebAPI)【发布时间】:2020-08-0920:03:09【问题描述】:我想在Asp.NetCoreWebApp(不是WebAPI)中使用JWT令牌对用户进行身份验证。如何存储JWT令牌... 查看详情

如何在 ASP.NET Core 的 web.config 中设置会话超时

】如何在ASP.NETCore的web.config中设置会话超时【英文标题】:howtosetSessionTimeoutinweb.configinASP.NETCore【发布时间】:2021-12-0119:36:27【问题描述】:如果用户暂停超过20分钟并且没有使用该网站,该网站会将她从帐户中删除,并且用户... 查看详情

如何在 Asp.NET Core WEB API 中使用 .Net (C#) 在 Payload 中创建具有自定义 JSON 声明的 JWT 令牌

】如何在Asp.NETCoreWEBAPI中使用.Net(C#)在Payload中创建具有自定义JSON声明的JWT令牌【英文标题】:HowtocreateaJWTtokenwithcustomJSONclaimsinPayloadusing.Net(C#)inAsp.NETCoreWEBAPI【发布时间】:2019-03-0913:02:38【问题描述】:我正在使用下面的代码和简... 查看详情

如何在 Web 应用程序(ASP.NET Core)中使用 TypeScript 导入语句而不是 <reference path...>?

】如何在Web应用程序(ASP.NETCore)中使用TypeScript导入语句而不是<referencepath...>?【英文标题】:HowtouseTypeScriptimportstatementinsteadof<referencepath...>inaWebapplication(ASP.NETCore)?【发布时间】:2021-11-2109:02:13【问题描述】:上下文... 查看详情

如何在 ASP.NET Core Web API 中使用 LINQ 仅从数据库中获取最新的 id?

】如何在ASP.NETCoreWebAPI中使用LINQ仅从数据库中获取最新的id?【英文标题】:HowtogetlatestidonlyfromdatabaseusingLINQinASP.NETCoreWebAPI?【发布时间】:2020-12-1303:30:46【问题描述】:Id|StName|Subject|1|Tom|Math|2|Jerry|Science|我是LINQ的新手,我试图... 查看详情

在 ASP .NET Core Web API 中使用没有身份的 cookie 身份验证时如何在登录时刷新 CSRF 令牌

】在ASP.NETCoreWebAPI中使用没有身份的cookie身份验证时如何在登录时刷新CSRF令牌【英文标题】:HowtorefreshCSRFtokenonloginwhenusingcookieauthenticationwithoutidentityinASP.NETCoreWebAPI【发布时间】:2020-09-1018:55:52【问题描述】:我有一个ASP.NETCore3.1... 查看详情

如何在 ASP.NET Core MVC 中请求和显示实时 Web api 数据?

】如何在ASP.NETCoreMVC中请求和显示实时Webapi数据?【英文标题】:Howtorequestanddisplayreal-timewebapidatainASP.NETCoreMVC?【发布时间】:2022-01-0909:05:44【问题描述】:我正在使用CoinGeckoapi获取ASP.NETCoreMVCWeb应用程序中几种加密货币的实时价... 查看详情

如何在 Visual Studio 2017 Web Proj ASP.NET Core 2.0 中禁用 Https

】如何在VisualStudio2017WebProjASP.NETCore2.0中禁用Https【英文标题】:HowToDisableHttpsinVisualStudio2017WebProjASP.NETCore2.0【发布时间】:2018-03-1210:24:16【问题描述】:我在VisualStudio2017中使用ASP.NETCore2.0创建了一个默认项目。我选择了带有MVC和... 查看详情

如何在我的 ASP.NET Core Web API 中启用 BSON 序列化?

】如何在我的ASP.NETCoreWebAPI中启用BSON序列化?【英文标题】:HowdoIenableBSONserializationinmyASP.NETCoreWebAPI?【发布时间】:2017-08-3010:00:54【问题描述】:我是ASP.NETCore和一般网络编程的新手。我刚刚成功完成了我的第一个基于RESTfull设... 查看详情

如何在 ASP.NET (Core 2) Web 应用程序中访问来自 AWS 的认证?

】如何在ASP.NET(Core2)Web应用程序中访问来自AWS的认证?【英文标题】:HowtoaccessacertificationfromAWSinaASP.NET(Core2)webapplication?【发布时间】:2018-04-2814:52:50【问题描述】:我正在尝试将ASP.NETCore2Web应用程序部署到AWSElasticBeanstalk。该应... 查看详情

如何在 ASP.NET Core 中获取 Web 根路径和内容根路径?

】如何在ASP.NETCore中获取Web根路径和内容根路径?【英文标题】:HowdoIgettheWebRootPathandtheContentRootPathinASP.NETCore?【发布时间】:2018-08-2217:11:18【问题描述】:我正在尝试在视图中添加根路径作为参数,因此我可以将其作为参数传递... 查看详情

如何在 ASP.NET Core Web API 2.1 中添加 WCF 服务引用

】如何在ASP.NETCoreWebAPI2.1中添加WCF服务引用【英文标题】:HowtoaddWCFServicereferenceinASP.NETCorewebAPI2.1【发布时间】:2021-03-2618:22:54【问题描述】:我尝试在我的ASP.netCoreWebAPI中添加服务引用,但出现以下错误:Unabletogeneratedeps.json,itmay... 查看详情

如何在 ASP.NET Core Web API 中配置 JSON 格式的缩进

】如何在ASP.NETCoreWebAPI中配置JSON格式的缩进【英文标题】:HowcaniconfigureJSONformatindentsinASP.NETCoreWebAPI【发布时间】:2016-12-3004:32:53【问题描述】:我如何配置ASP.NETCoreWebApi控制器以仅为Development环境返回格式良好的json?默认情况下... 查看详情