Supporto di WebSocket in ASP.NET Core
Nota
Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 di questo articolo.
Avviso
Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere Criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 8 di questo articolo.
Importante
Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.
Per la versione corrente, vedere la versione .NET 8 di questo articolo.
L'articolo contiene l'introduzione all'uso di oggetti WebSocket in ASP.NET Core. WebSocket (RFC 6455) è un protocollo che consente canali di comunicazione persistente bidirezionale su connessioni TCP. 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.
Visualizzare o scaricare il codice di esempio (come scaricare, come eseguire).
Supporto di WebSocket Http/2
L'uso di WebSocket su HTTP/2 sfrutta le nuove funzionalità, ad esempio:
- Compressione dell'intestazione.
- Multiplexing, che riduce il tempo e le risorse necessarie quando si effettuano più richieste al server.
Queste funzionalità supportate sono disponibili in in Kestrel tutte le piattaforme abilitate per HTTP/2. La negoziazione della versione è automatica nei browser e Kestrel, quindi non sono necessarie nuove API.
.NET 7 ha introdotto WebSockets su HTTP/2 supporto per Kestrel, il SignalR client JavaScript e SignalR con Blazor WebAssembly.
Nota
I WebSocket HTTP/2 usano le richieste CONNECT anziché GET, quindi le route e i controller personalizzati potrebbero richiedere l'aggiornamento. Per altre informazioni, vedere Aggiungere il supporto webSocket HTTP/2 per i controller esistenti in questo articolo.
Chrome e Edge hanno HTTP/2 WebSocket abilitati per impostazione predefinita ed è possibile abilitarlo in FireFox nella about:config
pagina con il network.http.spdy.websockets
flag .
I WebSocket sono stati originariamente progettati per HTTP/1.1, ma sono stati adattati per funzionare su HTTP/2. (RFC 8441)
SignalR
ASP.NET Core SignalR è una libreria che semplifica l'aggiunta di funzionalità Web in tempo reale alle app. Laddove possibile, usa oggetti WebSocket.
Per la maggior parte delle applicazioni, è consigliabile SignalR anziché webSocket non elaborati. SignalR:
- Fornisce il fallback del trasporto per gli ambienti in cui WebSocket non è disponibile.
- Fornisce un modello di app di chiamata di routine remota di base.
- Non presenta uno svantaggio significativo delle prestazioni rispetto all'uso di WebSocket non elaborati nella maggior parte degli scenari.
WebSockets su HTTP/2 è supportato per:
- ASP.NET client JavaScript core SignalR
- ASP.NET Core SignalR con Blazor WebAssembly
Per alcune app, gRPC in .NET offre un'alternativa ai WebSocket.
Prerequisiti
- Qualsiasi sistema operativo che supporta ASP.NET Core:
- Windows 7/Windows Server 2008 o versioni successive
- Linux
- macOS
- Se l'app viene eseguita in Windows con IIS:
- Windows 8/Windows Server 2012 o versioni successive
- IIS 8/IIS 8 Express
- I WebSocket devono essere abilitati. Vedere la sezione supporto di IIS/IIS Express.
- Se l'app viene eseguita in HTTP.sys:
- Windows 8/Windows Server 2012 o versioni successive
- Per i browser supportati, vedere È possibile usare.
Configurare il middleware
Aggiungere il middleware WebSockets in Program.cs
:
app.UseWebSockets();
È possibile configurare le impostazioni seguenti:
- KeepAliveInterval: la frequenza di invio di frame "ping" al client per garantire che i proxy tengano aperta la connessione. Il valore predefinito è due minuti.
- AllowedOrigins: elenco di valori dell'intestazione Origin consentiti per le richieste WebSocket. Per impostazione predefinita, tutte le origini sono consentite. Per altre informazioni, vedere Restrizione dell'origine WebSocket in questo articolo.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Accettare le richieste WebSocket
In un secondo momento nel ciclo di vita della richiesta (più avanti in Program.cs
o in un metodo di azione, ad esempio) controllare se si tratta di una richiesta WebSocket e accettare la richiesta WebSocket.
L'esempio seguente è riportato più avanti in Program.cs
:
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
else
{
await next(context);
}
});
Una richiesta WebSocket può arrivare in qualsiasi URL, ma questo codice di esempio accetta solo le richieste per /ws
.
Un approccio simile può essere adottato in un metodo controller:
public class WebSocketController : ControllerBase
{
[Route("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
Quando si usa un oggetto WebSocket, è necessario mantenere la pipeline middleware in esecuzione per la durata della connessione. Se si cerca di inviare o ricevere un messaggio WebSocket dopo che la pipeline middleware è terminata, si potrebbe ottenere un'eccezione simile alla seguente:
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. A tale scopo, usare TaskCompletionSource<TResult>. Passare TaskCompletionSource
al servizio in background e fare in modo che chiami TrySetResult al termine dell'uso dell'oggetto WebSocket. Quindi assegnare await
alla proprietà Task durante la richiesta, come mostrato nell'esempio seguente:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
L'eccezione chiusa WebSocket può verificarsi anche quando si torna troppo presto da un metodo di azione. Quando si accetta un socket in un metodo di azione, attendere il completamento del codice che usa il socket prima di tornare dal metodo di azione.
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. Usare sempre await
.
Aggiungere il supporto WebSocket HTTP/2 per i controller esistenti
.NET 7 ha introdotto WebSockets su HTTP/2 supporto per Kestrel, il SignalR client JavaScript e SignalR con Blazor WebAssembly. Http/2 WebSocket usano le richieste CONNECT anziché GET. Se in precedenza è stato usato [HttpGet("/path")]
nel metodo di azione del controller per le richieste Websocket, aggiornarlo in modo da usarlo [Route("/path")]
.
public class WebSocketController : ControllerBase
{
[Route("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
Compressione
Avviso
L'abilitazione della compressione sulle connessioni crittografate può rendere un'app soggetta a CRIME/BREACH attacchi.
Se si inviano informazioni riservate, evitare di abilitare la compressione o usare WebSocketMessageFlags.DisableCompression
quando si chiama WebSocket.SendAsync
.
Questo vale per entrambi i lati del WebSocket. Si noti che l'API WebSocket nel browser non ha la configurazione per disabilitare la compressione per invio.
Se si desidera la compressione dei messaggi su WebSocket, il codice accept deve specificare che consente la compressione come indicato di seguito:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
e WebSocketAcceptContext.DisableServerContextTakeover
sono opzioni avanzate che controllano il funzionamento della compressione.
La compressione viene negoziata tra il client e il server quando viene stabilita per la prima volta una connessione. Per altre informazioni sulla negoziazione, vedere Compression Extensions for WebSocket RFC (Estensioni di compressione per RFC WebSocket).
Nota
Se la negoziazione di compressione non viene accettata dal server o dal client, la connessione viene ancora stabilita. Tuttavia, la connessione non usa la compressione durante l'invio e la ricezione di messaggi.
Inviare e ricevere messaggi
Il AcceptWebSocketAsync
metodo aggiorna la connessione TCP a una connessione WebSocket e fornisce un WebSocket oggetto . Usare l'oggetto WebSocket
per inviare e ricevere messaggi.
Il codice illustrato in precedenza che accetta la richiesta WebSocket passa l'oggetto WebSocket
a un metodo Echo
. Il codice riceve un messaggio e lo reinvia immediatamente. I messaggi vengono inviati e ricevuti in un ciclo, fino a quando il client non chiude la connessione:
private static async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{
await webSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
Quando si accetta la connessione WebSocket prima che inizi il ciclo, la pipeline del middleware termina. Dopo la chiusura del socket, la pipeline si arresta, In altri termini, quando viene accettato il WebSocket, lo spostamento in avanti della richiesta nella pipeline si interrompe. Quando il ciclo viene completato e il socket viene chiuso, la richiesta torna ad avanzare nella pipeline.
Gestire le disconnessioni del client
Il server non viene informato automaticamente quando il client si disconnette a causa della perdita di connettività. Il server riceve un messaggio di disconnessione solo se inviato dal client, ma questo non è possibile in caso di interruzione della connessione Internet. 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.
Se il client non invia sempre messaggi e non si vuole scadere solo perché la connessione diventa inattiva, fare in modo che il client usi un timer per inviare un messaggio ping ogni X secondi. Nel server, se un messaggio non è arrivato entro 2*X secondi dopo quello precedente, terminare la connessione e segnalare che il client è disconnesso. Attendere il doppio del tempo previsto per tenere conto di eventuali ritardi della rete che potrebbero trattenere il messaggio ping.
Restrizione per le origini WebSocket
La protezione fornita da CORS non si applica agli oggetti WebSocket. I browser non:
- Eseguono richieste CORS preventive.
- Rispettano le restrizioni specificate nelle intestazioni
Access-Control
quando eseguono richieste WebSocket.
I browser, tuttavia, inviano l'intestazione Origin
quando rilasciano richieste WebSocket. Le applicazioni devono essere configurate per la convalida di queste intestazioni per assicurarsi che siano consentiti solo WebSocket provenienti dalle origini previste.
Se si ospita il server in "https://server.com" e ospitare il client in "https://client.com", aggiungere "https://client.com" all'elenco AllowedOrigins per WebSocket da verificare.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
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. Non usare queste intestazioni come meccanismo di autenticazione.
Supporto di IIS/IIS Express
Windows Server 2012 o versioni successive e Windows 8 o versioni successive con IIS/IIS Express 8 o versioni successive include il supporto per il protocollo WebSocket, ma non per WebSockets su HTTP/2.
Nota
Gli oggetti WebSocket sono sempre abilitati quando si usa IIS Express.
Abilitazione di oggetti WebSocket in IIS
Per abilitare il supporto per il protocollo WebSocket in Windows Server 2012 o versioni successive:
Nota
Questi passaggi non sono necessari quando si usa IIS Express
- Usare la procedura guidata Aggiungi ruoli e funzionalità accessibile tramite il menu Gestisci o il collegamento in Server Manager.
- Selezionare Installazione basata su ruoli o basata su funzionalità. Selezionare Avanti.
- Selezionare il server appropriato (il server locale è selezionato per impostazione predefinita). Selezionare Avanti.
- Espandere Server Web (IIS) nella struttura Ruoli, espandere Server Web, quindi Sviluppo applicazioni.
- Selezionare Protocollo WebSocket. Selezionare Avanti.
- Se non sono necessarie le funzionalità aggiuntive, selezionare Avanti.
- Selezionare Installa.
- Al termine dell'installazione, selezionare Chiudi per chiudere la procedura guidata.
Per abilitare il supporto per il protocollo WebSocket in Windows 8 o versioni successive:
Nota
Questi passaggi non sono necessari quando si usa IIS Express
- Passare a Pannello di controllo>Programmi>Programmi e funzionalità>Disattivare o attivare le funzionalità di Windows (a sinistra dello schermo).
- Espandere i nodi seguenti: Internet Information Services>Servizi Web>Funzionalità per lo sviluppo di applicazioni.
- Selezionare la funzionalità Protocollo WebSocket. Seleziona OK.
Disabilitare WebSocket quando si usa socket.io in Node.js
Se si usa il supporto WebSocket in socket.io in Node.js, disabilitare il modulo WebSocket IIS predefinito usando l'elemento webSocket
in web.config o applicationHost.config. Se questo passaggio non viene eseguito, il modulo WebSocket IIS tenta di gestire la comunicazione WebSocket anziché Node.js e l'app.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Esempio di app
L'app di esempio inclusa in questo articolo è un'app echo. Dispone di una pagina Web che effettua connessioni WebSocket e il server invia nuovamente tutti i messaggi ricevuti al client. L'app di esempio supporta WebSocket su HTTP/2 quando si usa un framework di destinazione di .NET 7 o versione successiva.
Eseguire l'app:
- Per eseguire l'app in Visual Studio: aprire il progetto di esempio in Visual Studio e premere CTRL+F5 per l'esecuzione senza il debugger.
- Per eseguire l'app in una shell dei comandi: eseguire il comando
dotnet run
e passare a un browser ahttp://localhost:<port>
.
La pagina Web mostra lo stato della connessione:
Selezionare Connect (Connetti) per inviare una richiesta WebSocket per l'URL indicato. Immettere un messaggio di prova e selezionare Send (Invia). Al termine dell'operazione, selezionare Close Socket (Chiudi socket). La sezione Comminication Log (Registrazione comunicazione) riporta tutte le azioni di apertura, invio e chiusura nel momento in cui vengono eseguite.
L'articolo contiene l'introduzione all'uso di oggetti WebSocket in ASP.NET Core. WebSocket (RFC 6455) è un protocollo che consente canali di comunicazione persistente bidirezionale su connessioni TCP. 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.
Visualizzare o scaricare il codice di esempio (come scaricare, come eseguire).
Supporto di WebSocket Http/2
L'uso di WebSocket su HTTP/2 sfrutta le nuove funzionalità, ad esempio:
- Compressione dell'intestazione.
- Multiplexing, che riduce il tempo e le risorse necessarie quando si effettuano più richieste al server.
Queste funzionalità supportate sono disponibili in in Kestrel tutte le piattaforme abilitate per HTTP/2. La negoziazione della versione è automatica nei browser e Kestrel, quindi non sono necessarie nuove API.
.NET 7 ha introdotto WebSockets su HTTP/2 supporto per Kestrel, il SignalR client JavaScript e SignalR con Blazor WebAssembly.
Nota
I WebSocket HTTP/2 usano le richieste CONNECT anziché GET, quindi le route e i controller personalizzati potrebbero richiedere l'aggiornamento. Per altre informazioni, vedere Aggiungere il supporto webSocket HTTP/2 per i controller esistenti in questo articolo.
Chrome e Edge hanno HTTP/2 WebSocket abilitati per impostazione predefinita ed è possibile abilitarlo in FireFox nella about:config
pagina con il network.http.spdy.websockets
flag .
I WebSocket sono stati originariamente progettati per HTTP/1.1, ma sono stati adattati per funzionare su HTTP/2. (RFC 8441)
SignalR
ASP.NET Core SignalR è una libreria che semplifica l'aggiunta di funzionalità Web in tempo reale alle app. Laddove possibile, usa oggetti WebSocket.
Per la maggior parte delle applicazioni, è consigliabile SignalR anziché webSocket non elaborati. SignalR:
- Fornisce il fallback del trasporto per gli ambienti in cui WebSocket non è disponibile.
- Fornisce un modello di app di chiamata di routine remota di base.
- Non presenta uno svantaggio significativo delle prestazioni rispetto all'uso di WebSocket non elaborati nella maggior parte degli scenari.
WebSockets su HTTP/2 è supportato per:
- ASP.NET client JavaScript core SignalR
- ASP.NET Core SignalR con Blazor WebAssembly
Per alcune app, gRPC in .NET offre un'alternativa ai WebSocket.
Prerequisiti
- Qualsiasi sistema operativo che supporta ASP.NET Core:
- Windows 7/Windows Server 2008 o versioni successive
- Linux
- macOS
- Se l'app viene eseguita in Windows con IIS:
- Windows 8/Windows Server 2012 o versioni successive
- IIS 8/IIS 8 Express
- I WebSocket devono essere abilitati. Vedere la sezione supporto di IIS/IIS Express.
- Se l'app viene eseguita in HTTP.sys:
- Windows 8/Windows Server 2012 o versioni successive
- Per i browser supportati, vedere È possibile usare.
Configurare il middleware
Aggiungere il middleware WebSockets in Program.cs
:
app.UseWebSockets();
È possibile configurare le impostazioni seguenti:
- KeepAliveInterval: la frequenza di invio di frame "ping" al client per garantire che i proxy tengano aperta la connessione. Il valore predefinito è due minuti.
- AllowedOrigins: elenco di valori dell'intestazione Origin consentiti per le richieste WebSocket. Per impostazione predefinita, tutte le origini sono consentite. Per altre informazioni, vedere Restrizione dell'origine WebSocket in questo articolo.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Accettare le richieste WebSocket
In un secondo momento nel ciclo di vita della richiesta (più avanti in Program.cs
o in un metodo di azione, ad esempio) controllare se si tratta di una richiesta WebSocket e accettare la richiesta WebSocket.
L'esempio seguente è riportato più avanti in Program.cs
:
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
else
{
await next(context);
}
});
Una richiesta WebSocket può arrivare in qualsiasi URL, ma questo codice di esempio accetta solo le richieste per /ws
.
Un approccio simile può essere adottato in un metodo controller:
public class WebSocketController : ControllerBase
{
[Route("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
Quando si usa un oggetto WebSocket, è necessario mantenere la pipeline middleware in esecuzione per la durata della connessione. Se si cerca di inviare o ricevere un messaggio WebSocket dopo che la pipeline middleware è terminata, si potrebbe ottenere un'eccezione simile alla seguente:
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. A tale scopo, usare TaskCompletionSource<TResult>. Passare TaskCompletionSource
al servizio in background e fare in modo che chiami TrySetResult al termine dell'uso dell'oggetto WebSocket. Quindi assegnare await
alla proprietà Task durante la richiesta, come mostrato nell'esempio seguente:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
L'eccezione chiusa WebSocket può verificarsi anche quando si torna troppo presto da un metodo di azione. Quando si accetta un socket in un metodo di azione, attendere il completamento del codice che usa il socket prima di tornare dal metodo di azione.
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. Usare sempre await
.
Aggiungere il supporto WebSocket HTTP/2 per i controller esistenti
.NET 7 ha introdotto WebSockets su HTTP/2 supporto per Kestrel, il SignalR client JavaScript e SignalR con Blazor WebAssembly. Http/2 WebSocket usano le richieste CONNECT anziché GET. Se in precedenza è stato usato [HttpGet("/path")]
nel metodo di azione del controller per le richieste Websocket, aggiornarlo in modo da usarlo [Route("/path")]
.
public class WebSocketController : ControllerBase
{
[Route("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
Compressione
Avviso
L'abilitazione della compressione sulle connessioni crittografate può rendere un'app soggetta a CRIME/BREACH attacchi.
Se si inviano informazioni riservate, evitare di abilitare la compressione o usare WebSocketMessageFlags.DisableCompression
quando si chiama WebSocket.SendAsync
.
Questo vale per entrambi i lati del WebSocket. Si noti che l'API WebSocket nel browser non ha la configurazione per disabilitare la compressione per invio.
Se si desidera la compressione dei messaggi su WebSocket, il codice accept deve specificare che consente la compressione come indicato di seguito:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
e WebSocketAcceptContext.DisableServerContextTakeover
sono opzioni avanzate che controllano il funzionamento della compressione.
La compressione viene negoziata tra il client e il server quando viene stabilita per la prima volta una connessione. Per altre informazioni sulla negoziazione, vedere Compression Extensions for WebSocket RFC (Estensioni di compressione per RFC WebSocket).
Nota
Se la negoziazione di compressione non viene accettata dal server o dal client, la connessione viene ancora stabilita. Tuttavia, la connessione non usa la compressione durante l'invio e la ricezione di messaggi.
Inviare e ricevere messaggi
Il AcceptWebSocketAsync
metodo aggiorna la connessione TCP a una connessione WebSocket e fornisce un WebSocket oggetto . Usare l'oggetto WebSocket
per inviare e ricevere messaggi.
Il codice illustrato in precedenza che accetta la richiesta WebSocket passa l'oggetto WebSocket
a un metodo Echo
. Il codice riceve un messaggio e lo reinvia immediatamente. I messaggi vengono inviati e ricevuti in un ciclo, fino a quando il client non chiude la connessione:
private static async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{
await webSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
Quando si accetta la connessione WebSocket prima che inizi il ciclo, la pipeline del middleware termina. Dopo la chiusura del socket, la pipeline si arresta, In altri termini, quando viene accettato il WebSocket, lo spostamento in avanti della richiesta nella pipeline si interrompe. Quando il ciclo viene completato e il socket viene chiuso, la richiesta torna ad avanzare nella pipeline.
Gestire le disconnessioni del client
Il server non viene informato automaticamente quando il client si disconnette a causa della perdita di connettività. Il server riceve un messaggio di disconnessione solo se inviato dal client, ma questo non è possibile in caso di interruzione della connessione Internet. 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.
Se il client non invia sempre messaggi e non si vuole scadere solo perché la connessione diventa inattiva, fare in modo che il client usi un timer per inviare un messaggio ping ogni X secondi. Nel server, se un messaggio non è arrivato entro 2*X secondi dopo quello precedente, terminare la connessione e segnalare che il client è disconnesso. Attendere il doppio del tempo previsto per tenere conto di eventuali ritardi della rete che potrebbero trattenere il messaggio ping.
Restrizione per le origini WebSocket
La protezione fornita da CORS non si applica agli oggetti WebSocket. I browser non:
- Eseguono richieste CORS preventive.
- Rispettano le restrizioni specificate nelle intestazioni
Access-Control
quando eseguono richieste WebSocket.
I browser, tuttavia, inviano l'intestazione Origin
quando rilasciano richieste WebSocket. Le applicazioni devono essere configurate per la convalida di queste intestazioni per assicurarsi che siano consentiti solo WebSocket provenienti dalle origini previste.
Se si ospita il server in "https://server.com" e ospitare il client in "https://client.com", aggiungere "https://client.com" all'elenco AllowedOrigins per WebSocket da verificare.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
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. Non usare queste intestazioni come meccanismo di autenticazione.
Supporto di IIS/IIS Express
Windows Server 2012 o versioni successive e Windows 8 o versioni successive con IIS/IIS Express 8 o versioni successive include il supporto per il protocollo WebSocket, ma non per WebSockets su HTTP/2.
Nota
Gli oggetti WebSocket sono sempre abilitati quando si usa IIS Express.
Abilitazione di oggetti WebSocket in IIS
Per abilitare il supporto per il protocollo WebSocket in Windows Server 2012 o versioni successive:
Nota
Questi passaggi non sono necessari quando si usa IIS Express
- Usare la procedura guidata Aggiungi ruoli e funzionalità accessibile tramite il menu Gestisci o il collegamento in Server Manager.
- Selezionare Installazione basata su ruoli o basata su funzionalità. Selezionare Avanti.
- Selezionare il server appropriato (il server locale è selezionato per impostazione predefinita). Selezionare Avanti.
- Espandere Server Web (IIS) nella struttura Ruoli, espandere Server Web, quindi Sviluppo applicazioni.
- Selezionare Protocollo WebSocket. Selezionare Avanti.
- Se non sono necessarie le funzionalità aggiuntive, selezionare Avanti.
- Selezionare Installa.
- Al termine dell'installazione, selezionare Chiudi per chiudere la procedura guidata.
Per abilitare il supporto per il protocollo WebSocket in Windows 8 o versioni successive:
Nota
Questi passaggi non sono necessari quando si usa IIS Express
- Passare a Pannello di controllo>Programmi>Programmi e funzionalità>Disattivare o attivare le funzionalità di Windows (a sinistra dello schermo).
- Espandere i nodi seguenti: Internet Information Services>Servizi Web>Funzionalità per lo sviluppo di applicazioni.
- Selezionare la funzionalità Protocollo WebSocket. Seleziona OK.
Disabilitare WebSocket quando si usa socket.io in Node.js
Se si usa il supporto WebSocket in socket.io in Node.js, disabilitare il modulo WebSocket IIS predefinito usando l'elemento webSocket
in web.config o applicationHost.config. Se questo passaggio non viene eseguito, il modulo WebSocket IIS tenta di gestire la comunicazione WebSocket anziché Node.js e l'app.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Esempio di app
L'app di esempio inclusa in questo articolo è un'app echo. Dispone di una pagina Web che effettua connessioni WebSocket e il server invia nuovamente tutti i messaggi ricevuti al client. L'app di esempio supporta WebSocket su HTTP/2 quando si usa un framework di destinazione di .NET 7 o versione successiva.
Eseguire l'app:
- Per eseguire l'app in Visual Studio: aprire il progetto di esempio in Visual Studio e premere CTRL+F5 per l'esecuzione senza il debugger.
- Per eseguire l'app in una shell dei comandi: eseguire il comando
dotnet run
e passare a un browser ahttp://localhost:<port>
.
La pagina Web mostra lo stato della connessione:
Selezionare Connect (Connetti) per inviare una richiesta WebSocket per l'URL indicato. Immettere un messaggio di prova e selezionare Send (Invia). Al termine dell'operazione, selezionare Close Socket (Chiudi socket). La sezione Comminication Log (Registrazione comunicazione) riporta tutte le azioni di apertura, invio e chiusura nel momento in cui vengono eseguite.
L'articolo contiene l'introduzione all'uso di oggetti WebSocket in ASP.NET Core. WebSocket (RFC 6455) è un protocollo che consente canali di comunicazione persistente bidirezionale su connessioni TCP. 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.
Visualizzare o scaricare il codice di esempio (come scaricare, come eseguire).
SignalR
ASP.NET Core SignalR è una libreria che semplifica l'aggiunta di funzionalità Web in tempo reale alle app. Laddove possibile, usa oggetti WebSocket.
Per la maggior parte delle applicazioni, è consigliabile SignalR usare WebSocket non elaborati. SignalR fornisce il fallback del trasporto per gli ambienti in cui WebSocket non è disponibile. Fornisce anche un modello di app di chiamata di procedura remota di base. Nella maggior parte degli scenari, inoltre, SignalR non presenta uno svantaggio significativo delle prestazioni rispetto all'uso di WebSocket non elaborati.
Per alcune app, gRPC in .NET offre un'alternativa ai WebSocket.
Prerequisiti
- Qualsiasi sistema operativo che supporta ASP.NET Core:
- Windows 7/Windows Server 2008 o versioni successive
- Linux
- macOS
- Se l'app viene eseguita in Windows con IIS:
- Windows 8/Windows Server 2012 o versioni successive
- IIS 8/IIS 8 Express
- I WebSocket devono essere abilitati. Vedere la sezione supporto di IIS/IIS Express.
- Se l'app viene eseguita in HTTP.sys:
- Windows 8/Windows Server 2012 o versioni successive
- Per i browser supportati, vedere È possibile usare.
Configurare il middleware
Aggiungere il middleware WebSockets in Program.cs
:
app.UseWebSockets();
È possibile configurare le impostazioni seguenti:
- KeepAliveInterval: la frequenza di invio di frame "ping" al client per garantire che i proxy tengano aperta la connessione. Il valore predefinito è due minuti.
- AllowedOrigins: elenco di valori dell'intestazione Origin consentiti per le richieste WebSocket. Per impostazione predefinita, tutte le origini sono consentite. Per altre informazioni, vedere Restrizione dell'origine WebSocket in questo articolo.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Accettare le richieste WebSocket
In un secondo momento nel ciclo di vita della richiesta (più avanti in Program.cs
o in un metodo di azione, ad esempio) controllare se si tratta di una richiesta WebSocket e accettare la richiesta WebSocket.
L'esempio seguente è riportato più avanti in Program.cs
:
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
else
{
await next(context);
}
});
Una richiesta WebSocket può arrivare in qualsiasi URL, ma questo codice di esempio accetta solo le richieste per /ws
.
Un approccio simile può essere adottato in un metodo controller:
public class WebSocketController : ControllerBase
{
[HttpGet("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
Quando si usa un oggetto WebSocket, è necessario mantenere la pipeline middleware in esecuzione per la durata della connessione. Se si cerca di inviare o ricevere un messaggio WebSocket dopo che la pipeline middleware è terminata, si potrebbe ottenere un'eccezione simile alla seguente:
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. A tale scopo, usare TaskCompletionSource<TResult>. Passare TaskCompletionSource
al servizio in background e fare in modo che chiami TrySetResult al termine dell'uso dell'oggetto WebSocket. Quindi assegnare await
alla proprietà Task durante la richiesta, come mostrato nell'esempio seguente:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
L'eccezione chiusa WebSocket può verificarsi anche quando si torna troppo presto da un metodo di azione. Quando si accetta un socket in un metodo di azione, attendere il completamento del codice che usa il socket prima di tornare dal metodo di azione.
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. Usare sempre await
.
Compressione
Avviso
L'abilitazione della compressione sulle connessioni crittografate può rendere un'app soggetta a CRIME/BREACH attacchi.
Se si inviano informazioni riservate, evitare di abilitare la compressione o usare WebSocketMessageFlags.DisableCompression
quando si chiama WebSocket.SendAsync
.
Questo vale per entrambi i lati del WebSocket. Si noti che l'API WebSocket nel browser non ha la configurazione per disabilitare la compressione per invio.
Se si desidera la compressione dei messaggi su WebSocket, il codice accept deve specificare che consente la compressione come indicato di seguito:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
e WebSocketAcceptContext.DisableServerContextTakeover
sono opzioni avanzate che controllano il funzionamento della compressione.
La compressione viene negoziata tra il client e il server quando viene stabilita per la prima volta una connessione. Per altre informazioni sulla negoziazione, vedere Compression Extensions for WebSocket RFC (Estensioni di compressione per RFC WebSocket).
Nota
Se la negoziazione di compressione non viene accettata dal server o dal client, la connessione viene ancora stabilita. Tuttavia, la connessione non usa la compressione durante l'invio e la ricezione di messaggi.
Inviare e ricevere messaggi
Il AcceptWebSocketAsync
metodo aggiorna la connessione TCP a una connessione WebSocket e fornisce un WebSocket oggetto . Usare l'oggetto WebSocket
per inviare e ricevere messaggi.
Il codice illustrato in precedenza che accetta la richiesta WebSocket passa l'oggetto WebSocket
a un metodo Echo
. Il codice riceve un messaggio e lo reinvia immediatamente. I messaggi vengono inviati e ricevuti in un ciclo, fino a quando il client non chiude la connessione:
private static async Task Echo(WebSocket webSocket)
{
var buffer = new byte[1024 * 4];
var receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
while (!receiveResult.CloseStatus.HasValue)
{
await webSocket.SendAsync(
new ArraySegment<byte>(buffer, 0, receiveResult.Count),
receiveResult.MessageType,
receiveResult.EndOfMessage,
CancellationToken.None);
receiveResult = await webSocket.ReceiveAsync(
new ArraySegment<byte>(buffer), CancellationToken.None);
}
await webSocket.CloseAsync(
receiveResult.CloseStatus.Value,
receiveResult.CloseStatusDescription,
CancellationToken.None);
}
Quando si accetta la connessione WebSocket prima che inizi il ciclo, la pipeline del middleware termina. Dopo la chiusura del socket, la pipeline si arresta, In altri termini, quando viene accettato il WebSocket, lo spostamento in avanti della richiesta nella pipeline si interrompe. Quando il ciclo viene completato e il socket viene chiuso, la richiesta torna ad avanzare nella pipeline.
Gestire le disconnessioni del client
Il server non viene informato automaticamente quando il client si disconnette a causa della perdita di connettività. Il server riceve un messaggio di disconnessione solo se inviato dal client, ma questo non è possibile in caso di interruzione della connessione Internet. 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.
Se il client non invia sempre messaggi e non si vuole scadere solo perché la connessione diventa inattiva, fare in modo che il client usi un timer per inviare un messaggio ping ogni X secondi. Nel server, se un messaggio non è arrivato entro 2*X secondi dopo quello precedente, terminare la connessione e segnalare che il client è disconnesso. Attendere il doppio del tempo previsto per tenere conto di eventuali ritardi della rete che potrebbero trattenere il messaggio ping.
Restrizione per le origini WebSocket
La protezione fornita da CORS non si applica agli oggetti WebSocket. I browser non:
- Eseguono richieste CORS preventive.
- Rispettano le restrizioni specificate nelle intestazioni
Access-Control
quando eseguono richieste WebSocket.
I browser, tuttavia, inviano l'intestazione Origin
quando rilasciano richieste WebSocket. Le applicazioni devono essere configurate per la convalida di queste intestazioni per assicurarsi che siano consentiti solo WebSocket provenienti dalle origini previste.
Se si ospita il server in "https://server.com" e ospitare il client in "https://client.com", aggiungere "https://client.com" all'elenco AllowedOrigins per WebSocket da verificare.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
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. Non usare queste intestazioni come meccanismo di autenticazione.
Supporto di IIS/IIS Express
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.
Nota
Gli oggetti WebSocket sono sempre abilitati quando si usa IIS Express.
Abilitazione di oggetti WebSocket in IIS
Per abilitare il supporto per il protocollo WebSocket in Windows Server 2012 o versioni successive:
Nota
Questi passaggi non sono necessari quando si usa IIS Express
- Usare la procedura guidata Aggiungi ruoli e funzionalità accessibile tramite il menu Gestisci o il collegamento in Server Manager.
- Selezionare Installazione basata su ruoli o basata su funzionalità. Selezionare Avanti.
- Selezionare il server appropriato (il server locale è selezionato per impostazione predefinita). Selezionare Avanti.
- Espandere Server Web (IIS) nella struttura Ruoli, espandere Server Web, quindi Sviluppo applicazioni.
- Selezionare Protocollo WebSocket. Selezionare Avanti.
- Se non sono necessarie le funzionalità aggiuntive, selezionare Avanti.
- Selezionare Installa.
- Al termine dell'installazione, selezionare Chiudi per chiudere la procedura guidata.
Per abilitare il supporto per il protocollo WebSocket in Windows 8 o versioni successive:
Nota
Questi passaggi non sono necessari quando si usa IIS Express
- Passare a Pannello di controllo>Programmi>Programmi e funzionalità>Disattivare o attivare le funzionalità di Windows (a sinistra dello schermo).
- Espandere i nodi seguenti: Internet Information Services>Servizi Web>Funzionalità per lo sviluppo di applicazioni.
- Selezionare la funzionalità Protocollo WebSocket. Seleziona OK.
Disabilitare WebSocket quando si usa socket.io in Node.js
Se si usa il supporto WebSocket in socket.io in Node.js, disabilitare il modulo WebSocket IIS predefinito usando l'elemento webSocket
in web.config o applicationHost.config. Se questo passaggio non viene eseguito, il modulo WebSocket IIS tenta di gestire la comunicazione WebSocket anziché Node.js e l'app.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Esempio di app
L'app di esempio inclusa in questo articolo è un'app echo. Dispone di una pagina Web che effettua connessioni WebSocket e il server invia nuovamente tutti i messaggi ricevuti al client. L'app di esempio non è configurata per l'esecuzione da Visual Studio con IIS Express, quindi eseguire l'app in una shell dei comandi con dotnet run
e passare a un browser a http://localhost:<port>
. La pagina Web mostra lo stato della connessione:
Selezionare Connect (Connetti) per inviare una richiesta WebSocket per l'URL indicato. Immettere un messaggio di prova e selezionare Send (Invia). Al termine dell'operazione, selezionare Close Socket (Chiudi socket). La sezione Comminication Log (Registrazione comunicazione) riporta tutte le azioni di apertura, invio e chiusura nel momento in cui vengono eseguite.
L'articolo contiene l'introduzione all'uso di oggetti WebSocket in ASP.NET Core. WebSocket (RFC 6455) è un protocollo che consente canali di comunicazione persistente bidirezionale su connessioni TCP. 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.
Visualizzare o scaricare il codice di esempio (procedura per il download). Come eseguire.
SignalR
ASP.NET Core SignalR è una libreria che semplifica l'aggiunta di funzionalità Web in tempo reale alle app. Laddove possibile, usa oggetti WebSocket.
Per la maggior parte delle applicazioni, è consigliabile SignalR usare WebSocket non elaborati. SignalR fornisce il fallback del trasporto per gli ambienti in cui WebSocket non è disponibile. Fornisce anche un modello di app di chiamata di procedura remota di base. Nella maggior parte degli scenari, inoltre, SignalR non presenta uno svantaggio significativo delle prestazioni rispetto all'uso di WebSocket non elaborati.
Per alcune app, gRPC in .NET offre un'alternativa ai WebSocket.
Prerequisiti
- Qualsiasi sistema operativo che supporta ASP.NET Core:
- Windows 7/Windows Server 2008 o versioni successive
- Linux
- macOS
- Se l'app viene eseguita in Windows con IIS:
- Windows 8/Windows Server 2012 o versioni successive
- IIS 8/IIS 8 Express
- I WebSocket devono essere abilitati. Vedere la sezione supporto di IIS/IIS Express.
- Se l'app viene eseguita in HTTP.sys:
- Windows 8/Windows Server 2012 o versioni successive
- Per i browser supportati, vedere È possibile usare.
Configurare il middleware
Aggiungere il middleware degli oggetti WebSocket nel metodo Configure
della classe Startup
:
app.UseWebSockets();
Nota
Se si desidera accettare richieste WebSocket in un controller, la chiamata a app.UseWebSockets
deve essere eseguita prima app.UseEndpoints
di .
È possibile configurare le impostazioni seguenti:
- KeepAliveInterval: la frequenza di invio di frame "ping" al client per garantire che i proxy tengano aperta la connessione. Il valore predefinito è due minuti.
- AllowedOrigins: elenco di valori dell'intestazione Origin consentiti per le richieste WebSocket. Per impostazione predefinita, tutte le origini sono consentite. Per informazioni dettagliate, vedere più avanti "Restrizione per le origini WebSocket".
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
app.UseWebSockets(webSocketOptions);
Accettare le richieste WebSocket
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.
L'esempio seguente è tratto da un punto successivo nel metodo Configure
:
app.Use(async (context, next) =>
{
if (context.Request.Path == "/ws")
{
if (context.WebSockets.IsWebSocketRequest)
{
using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync())
{
await Echo(context, webSocket);
}
}
else
{
context.Response.StatusCode = (int) HttpStatusCode.BadRequest;
}
}
else
{
await next();
}
});
Una richiesta WebSocket può arrivare in qualsiasi URL, ma questo codice di esempio accetta solo le richieste per /ws
.
Un approccio simile può essere adottato in un metodo controller:
public class WebSocketController : ControllerBase
{
[HttpGet("/ws")]
public async Task Get()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Echo(webSocket);
}
else
{
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
}
}
Quando si usa un oggetto WebSocket, è necessario mantenere la pipeline middleware in esecuzione per la durata della connessione. Se si cerca di inviare o ricevere un messaggio WebSocket dopo che la pipeline middleware è terminata, si potrebbe ottenere un'eccezione simile alla seguente:
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. A tale scopo, usare TaskCompletionSource<TResult>. Passare TaskCompletionSource
al servizio in background e fare in modo che chiami TrySetResult al termine dell'uso dell'oggetto WebSocket. Quindi assegnare await
alla proprietà Task durante la richiesta, come mostrato nell'esempio seguente:
app.Use(async (context, next) =>
{
using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync())
{
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
}
});
L'eccezione chiusa WebSocket può verificarsi anche quando si torna troppo presto da un metodo di azione. Quando si accetta un socket in un metodo di azione, attendere il completamento del codice che usa il socket prima di tornare dal metodo di azione.
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. Usare sempre await
.
Inviare e ricevere messaggi
Il AcceptWebSocketAsync
metodo aggiorna la connessione TCP a una connessione WebSocket e fornisce un WebSocket oggetto . Usare l'oggetto WebSocket
per inviare e ricevere messaggi.
Il codice illustrato in precedenza che accetta la richiesta WebSocket passa l'oggetto WebSocket
a un metodo Echo
. Il codice riceve un messaggio e lo reinvia immediatamente. I messaggi vengono inviati e ricevuti in un ciclo, fino a quando il client non chiude la connessione:
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. Dopo la chiusura del socket, la pipeline si arresta, In altri termini, quando viene accettato il WebSocket, lo spostamento in avanti della richiesta nella pipeline si interrompe. Quando il ciclo viene completato e il socket viene chiuso, la richiesta torna ad avanzare nella pipeline.
Gestire le disconnessioni del client
Il server non viene informato automaticamente quando il client si disconnette a causa della perdita di connettività. Il server riceve un messaggio di disconnessione solo se inviato dal client, ma questo non è possibile in caso di interruzione della connessione Internet. 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.
Se il client non invia sempre messaggi e non si vuole scadere solo perché la connessione diventa inattiva, fare in modo che il client usi un timer per inviare un messaggio ping ogni X secondi. Nel server, se un messaggio non è arrivato entro 2*X secondi dopo quello precedente, terminare la connessione e segnalare che il client è disconnesso. Attendere il doppio del tempo previsto per tenere conto di eventuali ritardi della rete che potrebbero trattenere il messaggio ping.
Nota
L'interno ManagedWebSocket
gestisce i fotogrammi Ping/Pong in modo implicito per mantenere attiva la connessione se l'opzione KeepAliveInterval
è maggiore di zero, che per impostazione predefinita è 30 secondi (TimeSpan.FromSeconds(30)
).
Restrizione per le origini WebSocket
La protezione fornita da CORS non si applica agli oggetti WebSocket. I browser non:
- Eseguono richieste CORS preventive.
- Rispettano le restrizioni specificate nelle intestazioni
Access-Control
quando eseguono richieste WebSocket.
I browser, tuttavia, inviano l'intestazione Origin
quando rilasciano richieste WebSocket. Le applicazioni devono essere configurate per la convalida di queste intestazioni per assicurarsi che siano consentiti solo WebSocket provenienti dalle origini previste.
Se si ospita il server in "https://server.com" e ospitare il client in "https://client.com", aggiungere "https://client.com" all'elenco AllowedOrigins per WebSocket da verificare.
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
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. Non usare queste intestazioni come meccanismo di autenticazione.
Supporto di IIS/IIS Express
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.
Nota
Gli oggetti WebSocket sono sempre abilitati quando si usa IIS Express.
Abilitazione di oggetti WebSocket in IIS
Per abilitare il supporto per il protocollo WebSocket in Windows Server 2012 o versioni successive:
Nota
Questi passaggi non sono necessari quando si usa IIS Express
- Usare la procedura guidata Aggiungi ruoli e funzionalità accessibile tramite il menu Gestisci o il collegamento in Server Manager.
- Selezionare Installazione basata su ruoli o basata su funzionalità. Selezionare Avanti.
- Selezionare il server appropriato (il server locale è selezionato per impostazione predefinita). Selezionare Avanti.
- Espandere Server Web (IIS) nella struttura Ruoli, espandere Server Web, quindi Sviluppo applicazioni.
- Selezionare Protocollo WebSocket. Selezionare Avanti.
- Se non sono necessarie le funzionalità aggiuntive, selezionare Avanti.
- Selezionare Installa.
- Al termine dell'installazione, selezionare Chiudi per chiudere la procedura guidata.
Per abilitare il supporto per il protocollo WebSocket in Windows 8 o versioni successive:
Nota
Questi passaggi non sono necessari quando si usa IIS Express
- Passare a Pannello di controllo>Programmi>Programmi e funzionalità>Disattivare o attivare le funzionalità di Windows (a sinistra dello schermo).
- Espandere i nodi seguenti: Internet Information Services>Servizi Web>Funzionalità per lo sviluppo di applicazioni.
- Selezionare la funzionalità Protocollo WebSocket. Seleziona OK.
Disabilitare WebSocket quando si usa socket.io in Node.js
Se si usa il supporto WebSocket in socket.io in Node.js, disabilitare il modulo WebSocket IIS predefinito usando l'elemento webSocket
in web.config o applicationHost.config. Se questo passaggio non viene eseguito, il modulo WebSocket IIS tenta di gestire la comunicazione WebSocket anziché Node.js e l'app.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Esempio di app
L'app di esempio inclusa in questo articolo è un'app echo. Dispone di una pagina Web che effettua connessioni WebSocket e il server invia nuovamente tutti i messaggi ricevuti al client. L'app di esempio non è configurata per l'esecuzione da Visual Studio con IIS Express, quindi eseguire l'app in una shell dei comandi con dotnet run
e passare a un browser a http://localhost:5000
. La pagina Web mostra lo stato della connessione:
Selezionare Connect (Connetti) per inviare una richiesta WebSocket per l'URL indicato. Immettere un messaggio di prova e selezionare Send (Invia). Al termine dell'operazione, selezionare Close Socket (Chiudi socket). La sezione Comminication Log (Registrazione comunicazione) riporta tutte le azioni di apertura, invio e chiusura nel momento in cui vengono eseguite.