【问题标题】:How to use web sockets in C# .NET Core 3.1?如何在 C# .NET Core 3.1 中使用 Web 套接字?
【发布时间】:2020-10-26 14:19:57
【问题描述】:

我正在尝试在我的 Web 应用程序中实现实时通知。只有我的网络应用中的管理员用户才能看到通知。
所以我在我的startup.cs文件which I think is not the right way中设置了网络套接字

Startup.cs

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();
                        
             }
             else
             {
                 context.Response.StatusCode = 400;
             }
         }
         else
         {
            await next();
         }
   });

这是我的 Javascript

window.onload = () => {
    if (/*User is Admin*/) {

        //Establish Websocket
        var socket = new WebSocket("wss:localhost:44301/ws");

        console.log(socket.readyState);

        socket.onerror = function (error) {
            console.log('WebSocket Error: ' + error);
        };

        socket.onopen = function (event) {          
            console.log("Socket connection opened")
        };

        // Handle messages sent by the server.
        socket.onmessage = function (event) {
            var data = event.data;
            console.log(data);
            //Draw some beautiful HTML notification
        };
    }
}

现在一切正常,但我不知道如何从我的服务器控制器发送消息,类似这样

[HttpGet]
public async Task<IActionResult> Foo(WebSocket webSocket)
{
    //What I am trying to do is send message from the open web socket connection.
    var buffer = new byte[1024 * 4];
    buffer = Encoding.UTF8.GetBytes("Foo");

    await webSocket.SendAsync(new ArraySegment<byte>(buffer),WebSocketMessageType.Text,true,CancellationToken.None);
    return View()
}

我不知道如何处理这个问题。我想做的是如果用户是管理员,打开 Web 套接字并从其他用户操作发送一些数据,(这意味着从我的一些控制器打开的 Web 套接字写入消息)

【问题讨论】:

  • 我建议使用 SignalR 来处理此类问题。

标签: c# asp.net-core .net-core websocket


【解决方案1】:

我有类似的问题,与消息系统。在 .NET CORE 中实现此目的的一种方法是使用 SIGNALR,因此您必须为要通信的每个用户创建连接。在您的情况下,每个用户都与管理员有联系。您应该在后端创建用于将此连接存储在哈希表中的类,以及一个实现发送方法的类。在 Startup.cs 我只有这个:

services.AddSignalR();
services.AddSingleton<ChatHub>();
services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            })
                   .AddJwtBearer(options =>
                   {
                       options.TokenValidationParameters = new TokenValidationParameters
                       {
                           ValidateIssuerSigningKey = true,
                           IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Secret").Value)),
                           ValidateIssuer = false,
                           ValidateAudience = false
                       };
                       options.Events = new JwtBearerEvents
                       {
                           OnMessageReceived = context =>
                           {
                               var accessToken = context.Request.Query["access_token"];

                               // If the request is for our hub...
                               var path = context.HttpContext.Request.Path;
                               if (!string.IsNullOrEmpty(accessToken) &&
                                   (path.StartsWithSegments("/chat")))
                               {
                                   // Read the token out of the query string
                                   context.Token = accessToken;
                               }
                               return Task.CompletedTask;
                           }
                       };
                   });

ChatHub 是扩展 Hub 的类,带有方法 send 和覆盖方法 OnConnectedAsyncOnDisconnectedAsync 用于从哈希中添加或删除连接。当您想发送消息时,您会在前面提出请求。 如果你想从你的控制器(如果你使用 JWT,这不是必需的)你只需要在你的控制器中注入 ChatHub 并调用发送消息。

ChatHub 这是重要的一行

await Clients.Client(connectionId).SendAsync("recievedMessage", messageToSend);

前面我使用了angular,所以代码不同,所以请访问这个链接:c# SignalR

【讨论】:

    【解决方案2】:

    SignalR 是你的朋友: https://docs.microsoft.com/en-us/aspnet/core/tutorials/signalr?view=aspnetcore-3.1

    你的客户应该是这样的:

    const connection = new signalR.HubConnectionBuilder()
        .withUrl("/MyHub")
        .configureLogging(signalR.LogLevel.Information)
        .build();
    
    connection.start()
        .then(function() {
        });
    
    connection.on("UpdateText",
        (data) => {
            if (!data) {
                console.log("No Update Rerading Data");
                return;
            }
            // Do whatever needs to be done
        });
    

    您的中心:

    public sealed class MyHub : Hub
    {
        public MyHub()
        { }
    }
    

    然后,您将创建一个可以从系统中的任何位置调用的 signalR 提供程序。 例如:

    await _signalRProvider
        .BroadcastAsync("MyHub", "UpdateText", "AdminsGroup", new { text = "Whatssaaaap!" })
        .ConfigureAwait(false);
    

    它会使用 HttpClient 将 json 类型的有效负载发布到特定组的 signalR 端点(或者如果您愿意,也可以使用)。

    $"{_endpoint}/api/v1/hubs/{hubName}/groups/{groupName}"
    

    当然,您需要注册集线器等。我跳过了这部分,因为它全部记录在上面的链接中。

    作为额外的: 如果您使用的是 Microsoft Azure,这会变得更加简单,因为您可以使用 SignalR 无服务器方法:https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-azure-functions-javascript

    【讨论】:

      【解决方案3】:

      为了能够从控制器发送到连接的 Web 套接字,您必须能够访问这些连接,作为保存这些连接的对象。

      This answer 展示了如何使用单独的类处理单个连接并保持连接处于活动状态(等待 client.RunAsync();)。

      完成此操作后,在您的情况下,您可能会创建另一个类 ConnectionContainer。它可能只有一个 ConcurrentDictionary 来保存所有传入连接。然后你必须让这个类对控制器可用。它可以通过依赖注入来完成。在 Startup.cs 中:

      public void ConfigureServices(IServiceCollection services)
      {
          services.AddSingleton(new ConnectionContainer());
          ...
      
      }
      

      并在控制器的ctor中添加ConnectionContainer作为参数并将其保存为私有字段:

       public class BroadcastController
       {
           private readonly ConnectionContainer _connectionContainer;
      
           public BroadcastController(ConnectionContainer connectionContainer)
           {
               _connectionContainer = connectionContainer;
           }
       }
      

      您的 Foo 方法可以直接使用连接,或者您可以在 ConnectionContainer 中实现广播方法(您可以重用它,并且它会被很好地封装)。

      顺便说一句,如果您想将 websockets 用于带有 asp.net 核心的 json/文本消息,我同意其他答案 - signalR 将更易于使用(代码更少)。如果您想完全控制 websocket 并根据您的需要对其进行完全优化,您的代码会更好,因为您可以将二进制数据直接传输到 web 客户端(signalR 不支持)。二进制数据可以在客户端使用DataView

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-05-04
        • 1970-01-01
        • 1970-01-01
        • 2021-09-25
        • 1970-01-01
        • 2022-07-06
        • 2020-05-06
        相关资源
        最近更新 更多