在 ASP.NET Core 中使用 WebSockets

作者 : 慕源网 本文共8255个字,预计阅读时间需要21分钟 发布时间: 2021-11-5 共663人阅读

本文中,我们将讨论 Web Socket以及如何在 ASP.NET Core 应用程序中使用 Web Socket。

在开始详细的工作之前,首先我们需要了解什么是Web Socket以及它是如何进行操作的。
实际上,Web Socket 是一种通过 TCP 连接提供全双工通信通道的通信协议。Web Socket 主要设计用于在 Web 服务器和 Web 浏览器中实现。但是,我们可以在任何客户端或服务器应用程序中实现 Web Socket,因为它完全独立于基于 TCP 的协议。它可用于聊天、股票行情、游戏等应用程序,以及您需要 Web 应用程序中的实时功能的任何地方。

Web Socket 的工作原理

Web Socket 实际上在服务器和客户端之间提供了持久或稳定的连接,以便双方可以随时向另一方发送数据。客户端通过称为 WebSocket 握手的过程建立 WebSocket 连接。此过程从客户端向服务器发送常规 HTTP 请求开始。

实时 Web 应用程序简史

在 Web 开发的早期,Web 站点基本上被开发为主要实现一个想法,即客户端可以向服务器发送获取任何类型数据的请求,另一方面,服务器必须满足将这些数据发送回服务器的请求。但是在 2005 年,随着 AJAX 的引入,Web 应用程序的这一基本概念发生了变化。

当时,人们希望借助 AJAX 开发双向 Web 应用程序或站点,以在 Client 和 Server 之间建立连接。Web 应用程序已经发展了很多,现在消耗的数据比以往任何时候都多。阻碍他们前进的最大因素是客户端发起事务的传统 HTTP 模型。为了克服这个问题,设计了许多不同的策略来允许服务器数据送到客户端。这些策略中最受欢迎的一种是长轮询
这涉及保持 HTTP 连接打开,直到服务器有一些数据要下推到客户端。但是在 HTTP 连接的情况下出现了问题。实际上,针对每一个 HTML 请求,一堆标头和 cookie 数据从客户端计算机传输到服务器,这会占用网络带宽。但是,如果我们想开发一个基于 Web 的应用程序,例如游戏或聊天应用程序,其中数据传输量是关键元素之一,那么最糟糕的部分是实际上不需要很多这些标头和 cookie 来满足客户端的请求.
我们真正需要的是一种创建持久、低延迟连接的方法,该连接可以支持由客户端或服务器发起的事务。这正是 Web Sockets 所提供的,在这篇文章中,您将学习如何在您自己的应用程序中使用它们。

何时使用

Web Socket 是一种先进的技术,可以在服务器和客户端浏览器之间建立交互连接。通过这种技术或 API,我们可以从客户端向服务器发送消息,也可以在不使用任何 TCP 或 HTTP 协议的情况下接收事件驱动的响应。ASP.NET SignalR 为实时功能提供了更丰富的应用程序模型,但它只能在 ASP.NET 上运行,而不是在 ASP.NET Core 上运行。SignalR 的核心版本正在开发中。但是您可能需要开发 SignalR 将提供的功能,例如:

  • 通过使用自动回退到替代传输方法来支持更广泛的浏览器版本。
  • 连接断开时自动重新连接。
  • 支持客户端调用服务器上的方法,反之亦然。
  • 支持扩展到多个服务器。 

如何使用它

  1. 从 Nuget 包管理器安装 Microsoft.AspNetCore.WebSockets 包。
  2. 配置中间件。
  3. 接受 WebSocket 请求。
  4. 发送和接收消息

配置中间件

在 Startup.cs 类的 Configure 方法中添加 WebSockets 中间件。

app.UseWebSockets();  

可以配置以下设置,

KeepAliveInterval

向客户端发送“ping”帧的频率,以确保代理保持连接打开。

ReceiveBufferSize

用于接收数据的缓冲区大小。只有高级用户才需要更改此设置,以便根据其数据大小进行性能调整。

var wsOptions = new WebSocketOptions()  
{  
   KeepAliveInterval = TimeSpan.FromSeconds(120),  
   ReceiveBufferSize = 4 * 1024  
};  
app.UseWebSockets(wsOptions);

接受 WebSocket 请求

在请求生命周期稍后的某个地方检查它是否是 Web Socket 请求并接受 Web Socket 请求。这个例子来自后面的 Configure 方法,

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();  
}  
});  

发送和接收消息

当我们想要将消息从服​​务器传输到客户端或反之亦然时,我们需要调用 AcceptWebSocketAsync 方法将 TCP 连接升级为 WebSocket 连接,它为我们提供了一个 WebSocket 对象。使用 WebSocket 对象发送和接收消息。该代码收到一条消息并立即发回相同的消息。它保持循环,直到客户端关闭连接。

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

当您在开始此循环之前接受 Web Socket时,中间件管道将结束。关闭socket后,管道展开,也就是说,当您接受 Web Socket时,请求将停止在管道中向前移动,就像您点击 MVC 操作时一样。但是当你完成这个循环并关闭socket时,请求会继续返回管道。

Program.cs

using System;  
using System.Collections.Generic;  
using System.IO;  
using System.Linq;  
using System.Threading.Tasks;  
using Microsoft.AspNetCore.Hosting;  
  
namespace Prog8_WebSocket  
{  
    public class Program  
    {  
        public static void Main(string[] args)  
        {  
            var host = new WebHostBuilder()  
                .UseKestrel()  
                .UseContentRoot(Directory.GetCurrentDirectory())  
                .UseIISIntegration()  
                .UseStartup<Startup>()  
                .Build();  
  
            host.Run();  
        }  
    }  
}  
Startup.cs
using Microsoft.AspNetCore.Builder;  
using Microsoft.AspNetCore.Hosting;  
using Microsoft.AspNetCore.Http;  
using Microsoft.Extensions.DependencyInjection;  
using Microsoft.Extensions.Logging;  
using System;  
using System.Net.WebSockets;  
using System.Threading;  
using System.Threading.Tasks;  
  
namespace Prog8_WebSocket  
{  
    public class Startup  
    {  
        public void ConfigureServices(IServiceCollection services)  
        {  
        }  
  
         public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)  
        {  
            loggerFactory.AddConsole(LogLevel.Debug);  
            if (env.IsDevelopment())  
            {  
                app.UseDeveloperExceptionPage();  
            }  
  
            app.UseWebSockets();  
  
            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();  
                }  
            });  
            app.UseFileServer();  
        }  
  
        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);  
        }  
  
    }  
} 
 Index.html
<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8" />  
    <title></title>  
    <style>  
        table {  
            border: 0  
        }  
  
        .commslog-data {  
            font-family: Consolas, Courier New, Courier, monospace;  
        }  
  
        .commslog-server {  
            background-color: red;  
            color: white  
        }  
  
        .commslog-client {  
            background-color: green;  
            color: white  
        }  
    </style>  
</head>  
<body>  
    <h1>WebSocket Sample Application</h1>  
    <p id="stateLabel">Ready to connect...</p>  
    <div>  
        <label for="connectionUrl">WebSocket Server URL:</label>  
        <input id="connectionUrl" />  
        <button id="connectButton" type="submit">Connect</button>  
    </div>  
    <p></p>  
    <div>  
        <label for="sendMessage">Message to send:</label>  
        <input id="sendMessage" disabled />  
        <button id="sendButton" type="submit" disabled>Send</button>  
        <button id="closeButton" disabled>Close Socket</button>  
    </div>  
    <h2>Communication Log</h2>  
    <table style="width: 800px">  
        <thead>  
            <tr>  
                <td style="width: 100px">From</td>  
                <td style="width: 100px">To</td>  
                <td>Data</td>  
            </tr>  
        </thead>  
        <tbody id="commsLog"></tbody>  
    </table>  
    <script>  
        var connectionForm = document.getElementById("connectionForm");  
        var connectionUrl = document.getElementById("connectionUrl");  
        var connectButton = document.getElementById("connectButton");  
        var stateLabel = document.getElementById("stateLabel");  
        var sendMessage = document.getElementById("sendMessage");  
        var sendButton = document.getElementById("sendButton");  
        var sendForm = document.getElementById("sendForm");  
        var commsLog = document.getElementById("commsLog");  
        var socket;  
        var scheme = document.location.protocol == "https:" ? "wss" : "ws";  
        var port = document.location.port ? (":" + document.location.port) : "";  
        connectionUrl.value = scheme + "://" + document.location.hostname + port + "/ws";  
  
        function updateState() {  
            function disable() {  
                sendMessage.disabled = true;  
                sendButton.disabled = true;  
                closeButton.disabled = true;  
            }  
  
            function enable() {  
                sendMessage.disabled = false;  
                sendButton.disabled = false;  
                closeButton.disabled = false;  
            }  
  
            connectionUrl.disabled = true;  
            connectButton.disabled = true;  
  
            if (!socket) {  
                disable();  
            } else {  
                switch (socket.readyState) {  
                    case WebSocket.CLOSED:  
                        stateLabel.innerHTML = "Closed";  
                        disable();  
                        connectionUrl.disabled = false;  
                        connectButton.disabled = false;  
                        break;  
                    case WebSocket.CLOSING:  
                        stateLabel.innerHTML = "Closing...";  
                        disable();  
                        break;  
                    case WebSocket.CONNECTING:  
                        stateLabel.innerHTML = "Connecting...";  
                        disable();  
                        break;  
                    case WebSocket.OPEN:  
                        stateLabel.innerHTML = "Open";  
                        enable();  
                        break;  
                    default:  
                        stateLabel.innerHTML = "Unknown WebSocket State: " + socket.readyState;  
                        disable();  
                        break;  
                }  
            }  
        }  
  
        closeButton.onclick = function () {  
            if (!socket || socket.readyState != WebSocket.OPEN) {  
                alert("socket not connected");  
            }  
            socket.close(1000, "Closing from client");  
        }  
  
        sendButton.onclick = function () {  
            if (!socket || socket.readyState != WebSocket.OPEN) {  
                alert("socket not connected");  
            }  
            var data = sendMessage.value;  
            socket.send(data);  
            commsLog.innerHTML += '<tr>' +  
                '<td class="commslog-client">Client</td>' +  
                '<td class="commslog-server">Server</td>' +  
                '<td class="commslog-data">' + data + '</td>'  
            '</tr>';  
        }  
  
        connectButton.onclick = function () {  
            stateLabel.innerHTML = "Connecting";  
            socket = new WebSocket(connectionUrl.value);  
            socket.onopen = function (event) {  
                updateState();  
                commsLog.innerHTML += '<tr>' +  
                    '<td colspan="3" class="commslog-data">Connection opened</td>' +  
                    '</tr>';  
            };  
  
            socket.onclose = function (event) {  
                updateState();  
                commsLog.innerHTML += '<tr>' +  
                    '<td colspan="3" class="commslog-data">Connection closed. Code: ' + event.code + '. Reason: ' + event.reason + '</td>' +  
                    '</tr>';  
            };  
  
            socket.onerror = updateState;  
            socket.onmessage = function (event) {  
                commsLog.innerHTML += '<tr>' +  
                    '<td class="commslog-server">Server</td>' +  
                    '<td class="commslog-client">Client</td>' +  
                    '<td class="commslog-data">' + event.data + '</td>'  
                '</tr>';  
            };  
        };  
    </script>  
</body>  
</html>

现在,从命令提示符运行代码。输出如下所示。


慕源网 » 在 ASP.NET Core 中使用 WebSockets

常见问题FAQ

程序仅供学习研究,请勿用于非法用途,不得违反国家法律,否则后果自负,一切法律责任与本站无关。
请仔细阅读以上条款再购买,拍下即代表同意条款并遵守约定,谢谢大家支持理解!

发表评论

开通VIP 享更多特权,建议使用QQ登录