关于c#:理解ASPNET-Core-中的WebSocket

在本文中,咱们将具体介绍RFC 6455 WebSocket标准,并配置一个通用的.NET 5应用程序通过WebSocket连贯与SignalR通信。

咱们将深刻底层的概念,以了解底层产生了什么。

对于WebSocket

引入WebSocket是为了实现客户端和服务器之间的双向通信。HTTP 1.0的一个痛点是每次向服务器发送申请时创立和敞开连贯。然而,在HTTP 1.1中,通过应用放弃连贯机制引入了长久连贯(RFC 2616)。这样,连贯能够被多个申请重用——这将缩小提早,因为服务器晓得客户端,它们不须要在每个申请的握手过程中启动。

WebSocket建设在HTTP 1.1标准之上,因为它容许长久连贯。因而,当你第一次创立WebSocket连贯时,它实质上是一个HTTP 1.1申请(稍后具体介绍)。这使得客户端和服务器之间可能进行实时通信。简略地说,下图形容了在发动(握手)、数据传输和敞开WS连贯期间产生的事件。咱们将在前面更深刻地钻研这些概念。

协定中蕴含了两局部:握手和数据传输。

握手

让咱们先从握手开始。

简略地说,WebSocket连贯基于单个端口上的HTTP(和作为传输的TCP)。上面是这些步骤的总结。

  1. 服务器必须监听传入的TCP套接字连贯。这能够是你调配的任何端口—通常是80或443。
  2. 客户端通过一个HTTP GET申请发动开始握手(否则服务器将不晓得与谁对话)——这是“WebSockets”中的“Web”局部。在消息报头中,客户端将申请服务器将连贯降级到WebSocket。
  3. 服务器发送一个握手响应,通知客户端它将把协定从HTTP更改为WebSocket。
  4. 客户端和服务器单方协商连贯细节。任何一方都能够退出。

上面是一个典型的关上(客户端)握手申请的样子。​​​​​​

GET /ws-endpoint HTTP/1.1
Host: example.com:80
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: L4kHN+1Bx7zKbxsDbqgzHw==
Sec-WebSocket-Version: 13

留神客户端是如何在申请中发送Connection: Upgrade和Upgrade: websocket报头的。

并且,服务器握手响应。​​​​​​

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: CTPN8jCb3BUjBjBtdjwSQCytuBo=

数据传输

咱们须要了解的下一个要害概念是数据传输。任何一方都能够在任何给定的工夫发送音讯——因为它是一个全双工通信协议。

音讯由一个或多个帧组成。帧的类型能够是文本(UTF-8)、二进制和管制帧(例如0x8 (Close)、0x9 (Ping)和0xA (Pong))。

装置

让咱们付诸行动,看看它是如何工作的。

首先创立一个 ASP.NET 5 WebAPI 我的项目。

dotnet new webapi -n WebSocketsTutorial
dotnet new sln
dotnet sln add WebSocketsTutorial

当初增加SignalR到我的项目中。

dotnet add WebSocketsTutorial/ package Microsoft.AspNet.SignalR

示例代码

咱们首先将WebSockets中间件增加到咱们的WebAPI应用程序中。关上Startup.cs,向Configure办法增加上面的代码。

在本教程中,我喜爱放弃简略。因而,我不打算探讨SignalR。它将齐全基于WebSocket通信。你也能够用原始的WebSockets实现同样的性能,如果你想让事件变得更简略,你不须要应用SignalR。

app.UseWebSockets();

接下来,咱们将删除默认的WeatherForecastController,并增加一个名为WebSocketsController的新控制器。留神,咱们将只是应用一个控制器action,而不是拦挡申请管道。

这个控制器的残缺代码如下所示。​​​​​​​


using System;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
namespace WebSocketsTutorial.Controllers{
    [ApiController]
    [Route("[controller]")]
    public class WebSocketsController : ControllerBase
    {
        private readonly ILogger<WebSocketsController> _logger;

        public WebSocketsController(ILogger<WebSocketsController> logger)
        {
            _logger = logger;
        }

        [HttpGet("/ws")]
        public async Task Get()
        {
          if (HttpContext.WebSockets.IsWebSocketRequest)
          {
              using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
              _logger.Log(LogLevel.Information, "WebSocket connection established");
              await Echo(webSocket);
          }
          else
          {
              HttpContext.Response.StatusCode = 400;
          }
        }
        
        private async Task Echo(WebSocket webSocket)
        {
            var buffer = new byte[1024 * 4];
            var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            _logger.Log(LogLevel.Information, "Message received from Client");

            while (!result.CloseStatus.HasValue)
            {
                var serverMsg = Encoding.UTF8.GetBytes($"Server: Hello. You said: {Encoding.UTF8.GetString(buffer)}");
                await webSocket.SendAsync(new ArraySegment<byte>(serverMsg, 0, serverMsg.Length), result.MessageType, result.EndOfMessage, CancellationToken.None);
                _logger.Log(LogLevel.Information, "Message sent to Client");

                result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
                _logger.Log(LogLevel.Information, "Message received from Client");
                
            }
            await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
            _logger.Log(LogLevel.Information, "WebSocket connection closed");
        }
    }
}

这是咱们所做的。

1、增加一个名为ws/的新路由。

2、查看以后申请是否通过WebSockets,否则抛出400。

3、期待,直到客户端发动申请。

4、进入一个循环,直到客户端敞开连贯。

5、在循环中,咱们将发送“Server: Hello. You said: <client’s message>”信息,并把它发回给客户端。

6、期待,直到客户端发送另一个申请。

留神,在初始握手之后,服务器不须要期待客户端发送申请来将音讯推送到客户端。让咱们运行应用程序,看看它是否工作。

dotnet run --project WebSocketsTutorial

运行应用程序后,请拜访https://localhost:5001/swagger/index.html,应该看到Swagger UI。
![上传中…]()
当初咱们将看到如何让客户端和服务器彼此通信。在这个演示中,我将应用Chrome的DevTools(关上新标签→查看或按F12→控制台标签)。然而,你能够抉择任何客户端。

首先,咱们将创立一个到服务器终结点的WebSocket连贯。

let webSocket = new WebSocket('wss://localhost:5001/ws');

它所做的是,在客户端和服务器之间发动一个连贯。wss://是WebSockets平安协定,因为咱们的WebAPI应用程序是通过TLS服务的。

而后,能够通过调用webSocket.send()办法发送音讯。你的控制台应该相似于上面的控制台。

让咱们认真看看WebSocket连贯

如果转到Network选项卡,则通过WS选项卡过滤掉申请,并单击最初一个称为WS的申请。

单击Messages选项卡并查看来回传递的音讯。在此期间,如果调用以下命令,将可能看到“This was sent from the Client!”。试试吧!

webSocket.send("Client: Hello");


如你所见,服务器的确须要期待客户端发送响应(即在初始握手之后),并且客户端能够发送音讯而不会被阻塞。这是全双工通信。咱们曾经探讨了WebSocket通信的数据传输方面。作为练习,你能够运行一个循环将音讯推送到客户机,以查看它的运行状况。

除此之外,服务器和客户端还能够通过ping-pong来查看客户端是否还活着。这是WebSockets中的一个理论个性!如果你真的想看看这些数据包,你能够应用像WireShark这样的工具来理解。

它是如何握手的?好吧,如果你跳转到Headers选项卡,你将可能看到咱们在这篇文章的第一局部谈到的申请-响应题目。

也能够尝试一下webSocket.close(),这样咱们就能够齐全笼罩open-data-close循环了。

论断

如果你对WebSocket的RFC感兴趣,请拜访RFC 6455并浏览。这篇文章只是涉及了WebSocket的外表,还有很多其余的货色咱们能够探讨,比方平安,负载平衡,代理等等。

原文链接:https://sahansera.dev/underst…

​​​​​​

【腾讯云】轻量 2核2G4M,首年65元

阿里云限时活动-云数据库 RDS MySQL  1核2G配置 1.88/月 速抢

本文由乐趣区整理发布,转载请注明出处,谢谢。

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据