Supporto di WebSocket in ASP.NET CoreWebSockets support in ASP.NET Core

Di Tom Dykstra e Andrew Stanton-NurseBy Tom Dykstra and Andrew Stanton-Nurse

L'articolo contiene l'introduzione all'uso di oggetti WebSocket in ASP.NET Core.This article explains how to get started with WebSockets in ASP.NET Core. WebSocket (RFC 6455) è un protocollo che consente canali di comunicazione persistente bidirezionale su connessioni TCP.WebSocket (RFC 6455) is a protocol that enables two-way persistent communication channels over TCP connections. Viene usato nelle app che sfruttano comunicazioni veloci e in tempo reale, ad esempio le app di chat, i dashboard e le app di gioco.It's used in apps that benefit from fast, real-time communication, such as chat, dashboard, and game apps.

Visualizzare o scaricare il codice di esempio (procedura per il download).View or download sample code (how to download). Come eseguire.How to run.

SignalRSignalR

ASP.NET Core SignalR è una libreria che consente di aggiungere in modo più semplice funzionalità Web in tempo reale alle app.ASP.NET Core SignalR is a library that simplifies adding real-time web functionality to apps. Laddove possibile, usa oggetti WebSocket.It uses WebSockets whenever possible.

Per la maggior parte delle applicazioni, è consigliabile preferire SignalR a oggetti WebSocket non elaborati.For most applications, we recommend SignalR over raw WebSockets. SignalR fornisce il fallback per il trasporto per gli ambienti in cui i WebSocket non sono disponibili.SignalR provides transport fallback for environments where WebSockets is not available. Fornisce inoltre un modello di app Remote Procedure Call semplice.It also provides a simple remote procedure call app model. Nella maggior parte degli scenari, inoltre, l'uso di SignalR non genera alcuno svantaggio significativo in termini di prestazioni rispetto all'uso di oggetti WebSocket non elaborati.And in most scenarios, SignalR has no significant performance disadvantage compared to using raw WebSockets.

PrerequisitiPrerequisites

  • ASP.NET Core 1.1 o versione successivaASP.NET Core 1.1 or later

  • Qualsiasi sistema operativo che supporta ASP.NET Core:Any OS that supports ASP.NET Core:

    • Windows 7/Windows Server 2008 o versioni successiveWindows 7 / Windows Server 2008 or later
    • LinuxLinux
    • macOSmacOS
  • Se l'app viene eseguita su Windows con IIS:If the app runs on Windows with IIS:

    • Windows 8/Windows Server 2012 o versioni successiveWindows 8 / Windows Server 2012 or later
    • IIS 8/IIS 8 ExpressIIS 8 / IIS 8 Express
    • Gli oggetti WebSocket devono essere abilitati (vedere la sezione Supporto di IIS/IIS Express).WebSockets must be enabled (See the IIS/IIS Express support section.).
  • Se l'app viene eseguita su HTTP.sys:If the app runs on HTTP.sys:

    • Windows 8/Windows Server 2012 o versioni successiveWindows 8 / Windows Server 2012 or later
  • Per i browser supportati, vedere https://caniuse.com/#feat=websockets.For supported browsers, see https://caniuse.com/#feat=websockets.

Pacchetto NuGetNuGet package

Installare il pacchetto Microsoft.AspNetCore.WebSockets.Install the Microsoft.AspNetCore.WebSockets package.

Configurare il middlewareConfigure the middleware

Aggiungere il middleware degli oggetti WebSocket nel metodo Configure della classe Startup:Add the WebSockets middleware in the Configure method of the Startup class:

app.UseWebSockets();

È possibile configurare le impostazioni seguenti:The following settings can be configured:

  • KeepAliveInterval: la frequenza di invio di frame "ping" al client per garantire che i proxy tengano aperta la connessione.KeepAliveInterval - How frequently to send "ping" frames to the client to ensure proxies keep the connection open. Il valore predefinito è due minuti.The default is two minutes.
  • ReceiveBufferSize: le dimensioni del buffer usato per ricevere dati.ReceiveBufferSize - The size of the buffer used to receive data. Gli utenti avanzati possono avere l'esigenza di modificare questa impostazione per ottimizzare le prestazioni in base alle dimensioni dei dati.Advanced users may need to change this for performance tuning based on the size of the data. L'impostazione predefinita è 4 KB.The default is 4 KB.

È possibile configurare le impostazioni seguenti:The following settings can be configured:

  • KeepAliveInterval: la frequenza di invio di frame "ping" al client per garantire che i proxy tengano aperta la connessione.KeepAliveInterval - How frequently to send "ping" frames to the client to ensure proxies keep the connection open. Il valore predefinito è due minuti.The default is two minutes.
  • ReceiveBufferSize: le dimensioni del buffer usato per ricevere dati.ReceiveBufferSize - The size of the buffer used to receive data. Gli utenti avanzati possono avere l'esigenza di modificare questa impostazione per ottimizzare le prestazioni in base alle dimensioni dei dati.Advanced users may need to change this for performance tuning based on the size of the data. L'impostazione predefinita è 4 KB.The default is 4 KB.
  • AllowedOrigins: elenco di valori dell'intestazione Origin consentiti per le richieste WebSocket.AllowedOrigins - A list of allowed Origin header values for WebSocket requests. Per impostazione predefinita, tutte le origini sono consentite.By default, all origins are allowed. Per informazioni dettagliate, vedere più avanti "Restrizione per le origini WebSocket".See "WebSocket origin restriction" below for details.
var webSocketOptions = new WebSocketOptions() 
{
    KeepAliveInterval = TimeSpan.FromSeconds(120),
    ReceiveBufferSize = 4 * 1024
};

app.UseWebSockets(webSocketOptions);

Accettare le richieste WebSocketAccept WebSocket requests

In un momento successivo nel ciclo di vita della richiesta, più avanti nel metodo Configure o in un metodo di azione, ad esempio, verificare che si tratti di una richiesta WebSocket e accettarla.Somewhere later in the request life cycle (later in the Configure method or in an action method, for example) check if it's a WebSocket request and accept the WebSocket request.

L'esempio seguente è tratto da un punto successivo nel metodo Configure:The following example is from later in the Configure method:

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

});

Una richiesta WebSocket può arrivare in qualsiasi URL, ma questo codice di esempio accetta solo le richieste per /ws.A WebSocket request could come in on any URL, but this sample code only accepts requests for /ws.

Quando si usa un oggetto WebSocket, è necessario mantenere la pipeline middleware in esecuzione per la durata della connessione.When using a WebSocket, you must keep the middleware pipeline running for the duration of the connection. Se si cerca di inviare o ricevere un messaggio WebSocket dopo che la pipeline middleware è terminata, si potrebbe ottenere un'eccezione simile alla seguente:If you attempt to send or receive a WebSocket message after the middleware pipeline ends, you may get an exception like the following:

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake. ---> System.ObjectDisposedException: Cannot write to the response body, the response has completed.
Object name: 'HttpResponseStream'.

Se si sta usando un servizio in background per scrivere i dati in un oggetto WebSocket, assicurarsi di mantenere la pipeline middleware in esecuzione.If you're using a background service to write data to a WebSocket, make sure you keep the middleware pipeline running. A tale scopo, usare TaskCompletionSource<TResult>.Do this by using a TaskCompletionSource<TResult>. Passare TaskCompletionSource al servizio in background e fare in modo che chiami TrySetResult al termine dell'uso dell'oggetto WebSocket.Pass the TaskCompletionSource to your background service and have it call TrySetResult when you finish with the WebSocket. Quindi assegnare await alla proprietà Task durante la richiesta, come mostrato nell'esempio seguente:Then await the Task property during the request, as shown in the following example:

app.Use(async (context, next) => {
    var socket = await context.WebSockets.AcceptWebSocketAsync();
    var socketFinishedTcs = new TaskCompletionSource<object>();

    BackgroundSocketProcessor.AddSocket(socket, socketFinishedTcs); 

    await socketFinishedTcs.Task;
});

L'eccezione di chiusura WebSocket può verificarsi anche se il ritorno da un metodo di azione avviene troppo presto.The WebSocket closed exception can also happen if you return too soon from an action method. Se si accetta un socket in un metodo di azione, attendere che il codice che usa il socket sia stato completato prima di tornare dal metodo dell'azione.If you accept a socket in an action method, wait for the code that uses the socket to complete before returning from the action method.

Per attendere il completamento del socket, non utilizzare mai Task.Wait(), Task.Result o chiamate di blocco simili perché ciò può provocare gravi problemi di threading.Never use Task.Wait(), Task.Result, or similar blocking calls to wait for the socket to complete, as that can cause serious threading issues. Usare sempre await.Always use await.

Inviare e ricevere messaggiSend and receive messages

Il metodo AcceptWebSocketAsync consente di aggiornare la connessione TCP a una connessione WebSocket e offre un oggetto WebSocket.The AcceptWebSocketAsync method upgrades the TCP connection to a WebSocket connection and provides a WebSocket object. Usare l'oggetto WebSocket per inviare e ricevere messaggi.Use the WebSocket object to send and receive messages.

Il codice illustrato in precedenza che accetta la richiesta WebSocket passa l'oggetto WebSocket a un metodo Echo.The code shown earlier that accepts the WebSocket request passes the WebSocket object to an Echo method. Il codice riceve un messaggio e lo reinvia immediatamente.The code receives a message and immediately sends back the same message. I messaggi vengono inviati e ricevuti in un ciclo, fino a quando il client non chiude la connessione:Messages are sent and received in a loop until the client closes the connection:

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

Quando si accetta la connessione WebSocket prima che inizi il ciclo, la pipeline del middleware termina.When accepting the WebSocket connection before beginning the loop, the middleware pipeline ends. Dopo la chiusura del socket, la pipeline si arresta,Upon closing the socket, the pipeline unwinds. In altri termini, quando viene accettato il WebSocket, lo spostamento in avanti della richiesta nella pipeline si interrompe.That is, the request stops moving forward in the pipeline when the WebSocket is accepted. Quando il ciclo viene completato e il socket viene chiuso, la richiesta torna ad avanzare nella pipeline.When the loop is finished and the socket is closed, the request proceeds back up the pipeline.

Gestire le disconnessioni del clientHandle client disconnects

Il server non viene informato automaticamente quando il client si disconnette a causa della perdita di connettività.The server is not automatically informed when the client disconnects due to loss of connectivity. Il server riceve un messaggio di disconnessione solo se inviato dal client, ma questo non è possibile in caso di interruzione della connessione Internet.The server receives a disconnect message only if the client sends it, which can't be done if the internet connection is lost. Se si vuole intervenire quando si verifica una situazione di questo tipo, impostare un timeout per segnalare che non sono stati ricevuti messaggi dal client entro un determinato intervallo di tempo.If you want to take some action when that happens, set a timeout after nothing is received from the client within a certain time window.

Se il client non invia messaggi con una certa frequenza e non si vuole impostare un timeout solo perché la connessione diventa inattiva, configurare un timer nel client in modo da inviare un messaggio ping ogni X secondi.If the client isn't always sending messages and you don't want to timeout just because the connection goes idle, have the client use a timer to send a ping message every X seconds. Nel server, se un messaggio non è arrivato entro 2*X secondi dal precedente, terminare la connessione e segnalare che il client si è disconnesso.On the server, if a message hasn't arrived within 2*X seconds after the previous one, terminate the connection and report that the client disconnected. Attendere il doppio del tempo previsto per tenere conto di eventuali ritardi della rete che potrebbero trattenere il messaggio ping.Wait for twice the expected time interval to leave extra time for network delays that might hold up the ping message.

Restrizione per le origini WebSocketWebSocket origin restriction

La protezione fornita da CORS non si applica agli oggetti WebSocket.The protections provided by CORS don't apply to WebSockets. I browser non:Browsers do not:

  • Eseguono richieste CORS preventive.Perform CORS pre-flight requests.
  • Rispettano le restrizioni specificate nelle intestazioni Access-Control quando eseguono richieste WebSocket.Respect the restrictions specified in Access-Control headers when making WebSocket requests.

I browser, tuttavia, inviano l'intestazione Origin quando rilasciano richieste WebSocket.However, browsers do send the Origin header when issuing WebSocket requests. Le applicazioni devono essere configurate per la convalida di queste intestazioni per assicurarsi che siano consentiti solo WebSocket provenienti dalle origini previste.Applications should be configured to validate these headers to ensure that only WebSockets coming from the expected origins are allowed.

Se si ospita il server in "https://server.com" e il client in "https://client.com", aggiungere "https://client.com" all'elenco AllowedOrigins per consentire la verifica dei WebSocket.If you're hosting your server on "https://server.com" and hosting your client on "https://client.com", add "https://client.com" to the AllowedOrigins list for WebSockets to verify.

var webSocketOptions = new WebSocketOptions()
{
    KeepAliveInterval = TimeSpan.FromSeconds(120),
    ReceiveBufferSize = 4 * 1024
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");

app.UseWebSockets(webSocketOptions);

Nota

L'intestazione Origin viene controllata dal client e, come l'intestazione Referer, può essere falsificata.The Origin header is controlled by the client and, like the Referer header, can be faked. Non usare queste intestazioni come meccanismo di autenticazione.Do not use these headers as an authentication mechanism.

Supporto di IIS/IIS ExpressIIS/IIS Express support

Windows Server 2012 o versioni successive e Windows 8 o versioni successive con IIS/IIS Express 8 o versioni successive includono il supporto per il protocollo WebSocket.Windows Server 2012 or later and Windows 8 or later with IIS/IIS Express 8 or later has support for the WebSocket protocol.

Nota

Gli oggetti WebSocket sono sempre abilitati quando si usa IIS Express.WebSockets are always enabled when using IIS Express.

Abilitazione di oggetti WebSocket in IISEnabling WebSockets on IIS

Per abilitare il supporto per il protocollo WebSocket in Windows Server 2012 o versioni successive:To enable support for the WebSocket protocol on Windows Server 2012 or later:

Nota

Questi passaggi non sono necessari quando si usa IIS ExpressThese steps are not required when using IIS Express

  1. Usare la procedura guidata Aggiungi ruoli e funzionalità accessibile tramite il menu Gestisci o il collegamento in Server Manager.Use the Add Roles and Features wizard from the Manage menu or the link in Server Manager.
  2. Selezionare Installazione basata su ruoli o basata su funzionalità.Select Role-based or Feature-based Installation. Scegliere Avanti.Select Next.
  3. Selezionare il server appropriato (il server locale è selezionato per impostazione predefinita).Select the appropriate server (the local server is selected by default). Scegliere Avanti.Select Next.
  4. Espandere Server Web (IIS) nella struttura Ruoli, espandere Server Web, quindi Sviluppo applicazioni.Expand Web Server (IIS) in the Roles tree, expand Web Server, and then expand Application Development.
  5. Selezionare Protocollo WebSocket.Select WebSocket Protocol. Scegliere Avanti.Select Next.
  6. Se non sono necessarie le funzionalità aggiuntive, selezionare Avanti.If additional features aren't needed, select Next.
  7. Selezionare Installa.Select Install.
  8. Al termine dell'installazione, selezionare Chiudi per chiudere la procedura guidata.When the installation completes, select Close to exit the wizard.

Per abilitare il supporto per il protocollo WebSocket in Windows 8 o versioni successive:To enable support for the WebSocket protocol on Windows 8 or later:

Nota

Questi passaggi non sono necessari quando si usa IIS ExpressThese steps are not required when using IIS Express

  1. Passare a Pannello di controllo > Programmi > Programmi e funzionalità > Attiva o disattiva funzionalità di Windows (sul lato sinistro dello schermo).Navigate to Control Panel > Programs > Programs and Features > Turn Windows features on or off (left side of the screen).
  2. Aprire i nodi seguenti: Internet Information Services > Servizi Web > Funzionalità per lo sviluppo di applicazioni.Open the following nodes: Internet Information Services > World Wide Web Services > Application Development Features.
  3. Selezionare la funzionalità Protocollo WebSocket.Select the WebSocket Protocol feature. Scegliere OK.Select OK.

Disabilitare WebSocket quando si usa socket.io su node.jsDisable WebSocket when using socket.io on Node.js

Se si usa il supporto WebSocket in socket.io su Node.js, disabilitare il modulo WebSocket IIS predefinito mediante l'elemento webSocket in web.config o in applicationHost.config. Se non viene eseguito questo passaggio, il modulo IIS WebSocket prova a gestire le comunicazioni WebSocket anziché Node.js e l'app.If using the WebSocket support in socket.io on Node.js, disable the default IIS WebSocket module using the webSocket element in web.config or applicationHost.config. If this step isn't performed, the IIS WebSocket module attempts to handle the WebSocket communication rather than Node.js and the app.

<system.webServer>
  <webSocket enabled="false" />
</system.webServer>

App di esempioSample app

L'app di esempio inclusa in questo articolo è un'app echo.The sample app that accompanies this article is an echo app. Ha una pagina Web che consente la connessione WebSocket e il server reinvia al client tutti i messaggi ricevuti.It has a web page that makes WebSocket connections, and the server resends any messages it receives back to the client. Eseguire l'app da un prompt dei comandi, in quanto non è configurata per l'esecuzione da Visual Studio con IIS Express, quindi passare a http://localhost:5000.Run the app from a command prompt (it's not set up to run from Visual Studio with IIS Express) and navigate to http://localhost:5000. Nella parte superiore sinistra della pagina Web viene visualizzato lo stato della connessione:The web page shows the connection status in the upper left:

Stato iniziale della pagina Web

Selezionare Connect (Connetti) per inviare una richiesta WebSocket per l'URL indicato.Select Connect to send a WebSocket request to the URL shown. Immettere un messaggio di prova e selezionare Send (Invia).Enter a test message and select Send. Al termine dell'operazione, selezionare Close Socket (Chiudi socket).When done, select Close Socket. La sezione Comminication Log (Registrazione comunicazione) riporta tutte le azioni di apertura, invio e chiusura nel momento in cui vengono eseguite.The Communication Log section reports each open, send, and close action as it happens.

Stato iniziale della pagina Web