Événements
19 nov., 23 h - 21 nov., 23 h
Participez à des sessions en ligne à Microsoft Ignite créées pour développer vos compétences et vous aider à résoudre les problèmes complexes d’aujourd’hui.
S’inscrire maintenantCe navigateur n’est plus pris en charge.
Effectuez une mise à niveau vers Microsoft Edge pour tirer parti des dernières fonctionnalités, des mises à jour de sécurité et du support technique.
Notes
Ceci n’est pas la dernière version de cet article. Pour la version actuelle, consultez la version .NET 9 de cet article.
Avertissement
Cette version d’ASP.NET Core n’est plus prise en charge. Pour plus d’informations, consultez la stratégie de support .NET et .NET Core. Pour la version actuelle, consultez la version .NET 9 de cet article.
Important
Ces informations portent sur la préversion du produit, qui est susceptible d’être en grande partie modifié avant sa commercialisation. Microsoft n’offre aucune garantie, expresse ou implicite, concernant les informations fournies ici.
Pour la version actuelle, consultez la version .NET 9 de cet article.
Cet article explique comment commencer avec les WebSockets dans ASP.NET Core. WebSocket (RFC 6455) est un protocole qui autorise des canaux de communication persistants bidirectionnels sur les connexions TCP. Son utilisation profite aux applications qui tirent parti d’une communication rapide et en temps réel, par exemple les applications de conversation, de tableau de bord et de jeu.
Affichez ou téléchargez l’exemple de code (procédure de téléchargement, procédure d’exécution).
L’utilisation des WebSockets sur HTTP/2 tire parti des nouvelles fonctionnalités, dont les suivantes :
Ces fonctionnalités prises en charge sont disponibles dans Kestrel sur toutes les plateformes prenant en charge HTTP/2. La négociation de version étant automatique dans les navigateurs et Kestrel, aucune nouvelle API n’est nécessaire.
.NET 7 a introduit la prise en charge de WebSockets sur HTTP/2 pour Kestrel, le client JavaScript SignalR et SignalR avec Blazor WebAssembly.
Notes
Les WebSockets HTTP/2 utilisent des demandes CONNECT plutôt que GET, de sorte que vos propres routes et contrôleurs peuvent nécessiter une mise à jour. Pour plus d’informations, consultez Ajouter la prise en charge des WebSockets HTTP/2 pour les contrôleurs existants dans cet article.
Les WebSockets HTTP/2 sont activés par défaut dans Chrome et Edge, et vous pouvez les activer dans FireFox sur la page about:config
avec l’indicateur network.http.spdy.websockets
.
Les WebSockets ont été initialement conçus pour HTTP/1.1, mais ils ont depuis été adaptés pour fonctionner sur HTTP/2. (RFC 8441)
ASP.NET Core SignalR est une bibliothèque qui simplifie l’ajout de fonctionnalités web en temps réel dans les applications. Elle utilise des WebSockets dans la mesure du possible.
Pour la plupart des applications, nous recommandons SignalR plutôt que des WebSockets bruts. SignalR:
Les WebSockets sur HTTP/2 sont pris en charge pour :
Pour certaines applications, gRPC sur .NET fournit une alternative aux WebSockets.
Ajoutez l’intergiciel WebSockets dans Program.cs
:
app.UseWebSockets();
Les paramètres suivants peuvent être configurés :
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Un peu plus tard dans le cycle de vie de la demande (plus loin dans Program.cs
ou dans une méthode d’action, par exemple), vérifiez s’il s’agit d’une demande WebSocket, puis acceptez-la.
L’exemple suivant est extrait d’une partie ultérieure dans 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);
}
});
Une requête WebSocket peut figurer sur toute URL, mais cet exemple de code accepte uniquement les requêtes pour /ws
.
Une approche similaire peut être adoptée dans une méthode de contrôleur :
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;
}
}
Lorsque vous utilisez un WebSocket, vous devez conserver le pipeline de l’intergiciel en cours d’exécution pendant la durée de la connexion. Si vous tentez d’envoyer ou de recevoir un message WebSocket une fois que le pipeline de l’intergiciel se termine, vous pouvez obtenir une exception comme suit :
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'.
Si vous utilisez un service en arrière-plan pour écrire des données dans un WebSocket, assurez-vous que vous conservez le pipeline de l’intergiciel en cours d’exécution. Pour ce faire, utilisez un TaskCompletionSource<TResult>. Passez le TaskCompletionSource
à l’arrière-plan de votre service et faites appeler TrySetResult lorsque vous avez terminé avec le WebSocket. Utilisez ensuite await
sur la propriété Task durant l’envoi de la requête, comme indiqué dans l’exemple suivant :
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
L’exception relative à la fermeture de WebSocket peut également se produire si vous effectuez trop tôt un retour à partir d’une méthode d’action. Si vous acceptez un socket dans une méthode d’action, attendez que le code qui l’utilise soit entièrement exécuté, avant d’effectuer un retour à partir de la méthode d’action.
N’utilisez jamais Task.Wait
, Task.Result
ou des appels de blocage similaires pour attendre la fin de l’exécution du socket, car cela peut entraîner de graves problèmes de thread. Utilisez toujours await
.
.NET 7 a introduit la prise en charge de WebSockets sur HTTP/2 pour Kestrel, le client JavaScript SignalR et SignalR avec Blazor WebAssembly. Les WebSockets HTTP/2 utilisent des requêtes CONNECT plutôt que GET. Si vous avez déjà utilisé [HttpGet("/path")]
sur votre méthode d’action de contrôleur pour les demandes Websocket, mettez-la à jour pour utiliser [Route("/path")]
à la place.
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;
}
}
Avertissement
L’activation de la compression sur des connexions chiffrées peut exposer une application à des attaques CRIME/BREACH.
Si vous envoyez des informations sensibles, évitez d’activer la compression ou utilisez WebSocketMessageFlags.DisableCompression
lorsque vous appelez WebSocket.SendAsync
.
Cela s’applique aux deux côtés du WebSocket. Notez que l’API WebSockets dans le navigateur n’a pas de configuration pour désactiver la compression par envoi.
Si la compression des messages sur WebSockets est souhaitée, le code d’acceptation doit spécifier qu’il autorise la compression comme suit :
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
et WebSocketAcceptContext.DisableServerContextTakeover
sont des options avancées qui contrôlent le fonctionnement de la compression.
La compression est négociée entre le client et le serveur lors de l’établissement initial d’une connexion. Vous pouvez en apprendre davantage sur la négociation dans le document RFC sur les extensions de compression pour WebSocket.
Notes
Si la négociation de compression n’est pas acceptée par le serveur ou le client, la connexion est toujours établie. Toutefois, la connexion n’utilise pas la compression lors de l’envoi et de la réception de messages.
La méthode AcceptWebSocketAsync
met à niveau la connexion TCP vers une connexion WebSocket, et fournit un objet WebSocket. Utilisez l’objet WebSocket
pour envoyer et recevoir des messages.
Le code montré précédemment qui accepte la requête WebSocket passe l’objet WebSocket
à une méthode Echo
. Le code reçoit un message et renvoie immédiatement le même message. Les messages sont envoyés et reçus dans une boucle jusqu’à ce que le client ferme la connexion :
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);
}
Quand vous acceptez le WebSocket avant de commencer cette boucle, le pipeline de middlewares se termine. À la fermeture du socket, le pipeline se déroule. Autrement dit, la requête cesse d’avancer dans le pipeline quand le WebSocket est accepté. Quand la boucle est terminée et que le socket est fermé, la requête recule dans le pipeline.
Le serveur n’est pas automatiquement informé lorsque le client se déconnecte en raison d’une perte de connectivité. Le serveur reçoit un message de déconnexion uniquement si le client l’envoie, ce qui n’est pas possible si la connexion Internet est perdue. Si vous souhaitez prendre des mesures quand cela se produit, définissez un délai d’attente lorsque rien n’est reçu du client dans un certain laps de temps.
Si le client n’envoie pas toujours de messages et que vous ne souhaitez pas appliquer le délai d’attente uniquement parce que la connexion devient inactive, demandez au client d’utiliser un minuteur pour envoyer un message ping toutes les X secondes. Sur le serveur, si un message n’arrive pas dans un délai de 2*X secondes après le précédent, mettez fin à la connexion et signalez que le client s’est déconnecté. Attendez deux fois la durée de l’intervalle de temps attendu pour autoriser les délais réseau qui peuvent retarder le message ping.
Les protections fournies par CORS ne s’appliquent pas aux WebSockets. Les navigateurs :
Access-Control
quand ils effectuent des requêtes WebSocket.Toutefois, les navigateurs envoient l’en-tête Origin
au moment de l’émission des requêtes WebSocket. Les applications doivent être configurées de manière à valider ces en-têtes, le but étant de vérifier que seuls les WebSockets provenant des origines attendues sont autorisés.
Si vous hébergez votre serveur sur « https://server.com" et votre client sur « https://client.com", ajoutez « https://client.com" à la liste AllowedOrigins à vérifier par les WebSockets.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
Notes
L’en-tête Origin
est contrôlé par le client et, comme l’en-tête Referer
, peut être falsifié. N’utilisez pas ces en-têtes comme mécanisme d’authentification.
Windows Server 2012 ou version ultérieure et Windows 8 ou version ultérieure avec IIS/IIS Express 8 ou version ultérieure prennent en charge le protocole WebSocket, mais pas les WebSockets sur HTTP/2.
Notes
WebSockets sont toujours activés lorsque vous utilisez IIS Express.
Pour activer la prise en charge du protocole WebSocket sur Windows Server 2012 ou ultérieur :
Notes
Ces étapes ne sont pas nécessaires si vous utilisez IIS Express
Pour activer la prise en charge du protocole WebSocket sur Windows 8 ou ultérieur :
Notes
Ces étapes ne sont pas nécessaires si vous utilisez IIS Express
Si vous utilisez la prise en charge de WebSocket dans socket.io sur Node.js, désactivez le module WebSocket IIS par défaut à l’aide de l’élément webSocket
dans web.config ou applicationHost.config. Si cette étape n’est pas effectuée, le module WebSocket IIS tente de gérer la communication WebSocket, au lieu de celle de Node.js et de l’application.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
L’exemple d’application qui accompagne cet article est une application d’écho. Elle comporte une page web qui établit des connexions WebSocket, et le serveur renvoie au client les messages qu’il reçoit. L’exemple d’application prend en charge les WebSockets sur HTTP/2 lors de l’utilisation d’un framework ciblé de .NET 7 ou version ultérieure.
Exécutez l’application :
dotnet run
et accédez dans un navigateur à http://localhost:<port>
.La page web affiche l’état de connexion :
Sélectionnez Connect (Connexion) pour envoyer une requête WebSocket à l’URL affichée. Entrez un message de test, puis sélectionnez Send (Envoyer). Quand vous avez terminé, sélectionnez Close Socket (Fermer le socket). La section Communication Log (Journal des communications) signale chaque action d’ouverture, d’envoi et de fermeture en temps réel.
Cet article explique comment commencer avec les WebSockets dans ASP.NET Core. WebSocket (RFC 6455) est un protocole qui autorise des canaux de communication persistants bidirectionnels sur les connexions TCP. Son utilisation profite aux applications qui tirent parti d’une communication rapide et en temps réel, par exemple les applications de conversation, de tableau de bord et de jeu.
Affichez ou téléchargez l’exemple de code (procédure de téléchargement, procédure d’exécution).
L’utilisation des WebSockets sur HTTP/2 tire parti des nouvelles fonctionnalités, dont les suivantes :
Ces fonctionnalités prises en charge sont disponibles dans Kestrel sur toutes les plateformes prenant en charge HTTP/2. La négociation de version étant automatique dans les navigateurs et Kestrel, aucune nouvelle API n’est nécessaire.
.NET 7 a introduit la prise en charge de WebSockets sur HTTP/2 pour Kestrel, le client JavaScript SignalR et SignalR avec Blazor WebAssembly.
Notes
Les WebSockets HTTP/2 utilisent des demandes CONNECT plutôt que GET, de sorte que vos propres routes et contrôleurs peuvent nécessiter une mise à jour. Pour plus d’informations, consultez Ajouter la prise en charge des WebSockets HTTP/2 pour les contrôleurs existants dans cet article.
Les WebSockets HTTP/2 sont activés par défaut dans Chrome et Edge, et vous pouvez les activer dans FireFox sur la page about:config
avec l’indicateur network.http.spdy.websockets
.
Les WebSockets ont été initialement conçus pour HTTP/1.1, mais ils ont depuis été adaptés pour fonctionner sur HTTP/2. (RFC 8441)
ASP.NET Core SignalR est une bibliothèque qui simplifie l’ajout de fonctionnalités web en temps réel dans les applications. Elle utilise des WebSockets dans la mesure du possible.
Pour la plupart des applications, nous recommandons SignalR plutôt que des WebSockets bruts. SignalR:
Les WebSockets sur HTTP/2 sont pris en charge pour :
Pour certaines applications, gRPC sur .NET fournit une alternative aux WebSockets.
Ajoutez l’intergiciel WebSockets dans Program.cs
:
app.UseWebSockets();
Les paramètres suivants peuvent être configurés :
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Un peu plus tard dans le cycle de vie de la demande (plus loin dans Program.cs
ou dans une méthode d’action, par exemple), vérifiez s’il s’agit d’une demande WebSocket, puis acceptez-la.
L’exemple suivant est extrait d’une partie ultérieure dans 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);
}
});
Une requête WebSocket peut figurer sur toute URL, mais cet exemple de code accepte uniquement les requêtes pour /ws
.
Une approche similaire peut être adoptée dans une méthode de contrôleur :
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;
}
}
Lorsque vous utilisez un WebSocket, vous devez conserver le pipeline de l’intergiciel en cours d’exécution pendant la durée de la connexion. Si vous tentez d’envoyer ou de recevoir un message WebSocket une fois que le pipeline de l’intergiciel se termine, vous pouvez obtenir une exception comme suit :
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'.
Si vous utilisez un service en arrière-plan pour écrire des données dans un WebSocket, assurez-vous que vous conservez le pipeline de l’intergiciel en cours d’exécution. Pour ce faire, utilisez un TaskCompletionSource<TResult>. Passez le TaskCompletionSource
à l’arrière-plan de votre service et faites appeler TrySetResult lorsque vous avez terminé avec le WebSocket. Utilisez ensuite await
sur la propriété Task durant l’envoi de la requête, comme indiqué dans l’exemple suivant :
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
L’exception relative à la fermeture de WebSocket peut également se produire si vous effectuez trop tôt un retour à partir d’une méthode d’action. Si vous acceptez un socket dans une méthode d’action, attendez que le code qui l’utilise soit entièrement exécuté, avant d’effectuer un retour à partir de la méthode d’action.
N’utilisez jamais Task.Wait
, Task.Result
ou des appels de blocage similaires pour attendre la fin de l’exécution du socket, car cela peut entraîner de graves problèmes de thread. Utilisez toujours await
.
.NET 7 a introduit la prise en charge de WebSockets sur HTTP/2 pour Kestrel, le client JavaScript SignalR et SignalR avec Blazor WebAssembly. Les WebSockets HTTP/2 utilisent des requêtes CONNECT plutôt que GET. Si vous avez déjà utilisé [HttpGet("/path")]
sur votre méthode d’action de contrôleur pour les demandes Websocket, mettez-la à jour pour utiliser [Route("/path")]
à la place.
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;
}
}
Avertissement
L’activation de la compression sur des connexions chiffrées peut exposer une application à des attaques CRIME/BREACH.
Si vous envoyez des informations sensibles, évitez d’activer la compression ou utilisez WebSocketMessageFlags.DisableCompression
lorsque vous appelez WebSocket.SendAsync
.
Cela s’applique aux deux côtés du WebSocket. Notez que l’API WebSockets dans le navigateur n’a pas de configuration pour désactiver la compression par envoi.
Si la compression des messages sur WebSockets est souhaitée, le code d’acceptation doit spécifier qu’il autorise la compression comme suit :
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
et WebSocketAcceptContext.DisableServerContextTakeover
sont des options avancées qui contrôlent le fonctionnement de la compression.
La compression est négociée entre le client et le serveur lors de l’établissement initial d’une connexion. Vous pouvez en apprendre davantage sur la négociation dans le document RFC sur les extensions de compression pour WebSocket.
Notes
Si la négociation de compression n’est pas acceptée par le serveur ou le client, la connexion est toujours établie. Toutefois, la connexion n’utilise pas la compression lors de l’envoi et de la réception de messages.
La méthode AcceptWebSocketAsync
met à niveau la connexion TCP vers une connexion WebSocket, et fournit un objet WebSocket. Utilisez l’objet WebSocket
pour envoyer et recevoir des messages.
Le code montré précédemment qui accepte la requête WebSocket passe l’objet WebSocket
à une méthode Echo
. Le code reçoit un message et renvoie immédiatement le même message. Les messages sont envoyés et reçus dans une boucle jusqu’à ce que le client ferme la connexion :
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);
}
Quand vous acceptez le WebSocket avant de commencer cette boucle, le pipeline de middlewares se termine. À la fermeture du socket, le pipeline se déroule. Autrement dit, la requête cesse d’avancer dans le pipeline quand le WebSocket est accepté. Quand la boucle est terminée et que le socket est fermé, la requête recule dans le pipeline.
Le serveur n’est pas automatiquement informé lorsque le client se déconnecte en raison d’une perte de connectivité. Le serveur reçoit un message de déconnexion uniquement si le client l’envoie, ce qui n’est pas possible si la connexion Internet est perdue. Si vous souhaitez prendre des mesures quand cela se produit, définissez un délai d’attente lorsque rien n’est reçu du client dans un certain laps de temps.
Si le client n’envoie pas toujours de messages et que vous ne souhaitez pas appliquer le délai d’attente uniquement parce que la connexion devient inactive, demandez au client d’utiliser un minuteur pour envoyer un message ping toutes les X secondes. Sur le serveur, si un message n’arrive pas dans un délai de 2*X secondes après le précédent, mettez fin à la connexion et signalez que le client s’est déconnecté. Attendez deux fois la durée de l’intervalle de temps attendu pour autoriser les délais réseau qui peuvent retarder le message ping.
Les protections fournies par CORS ne s’appliquent pas aux WebSockets. Les navigateurs :
Access-Control
quand ils effectuent des requêtes WebSocket.Toutefois, les navigateurs envoient l’en-tête Origin
au moment de l’émission des requêtes WebSocket. Les applications doivent être configurées de manière à valider ces en-têtes, le but étant de vérifier que seuls les WebSockets provenant des origines attendues sont autorisés.
Si vous hébergez votre serveur sur « https://server.com" et votre client sur « https://client.com", ajoutez « https://client.com" à la liste AllowedOrigins à vérifier par les WebSockets.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
Notes
L’en-tête Origin
est contrôlé par le client et, comme l’en-tête Referer
, peut être falsifié. N’utilisez pas ces en-têtes comme mécanisme d’authentification.
Windows Server 2012 ou version ultérieure et Windows 8 ou version ultérieure avec IIS/IIS Express 8 ou version ultérieure prennent en charge le protocole WebSocket, mais pas les WebSockets sur HTTP/2.
Notes
WebSockets sont toujours activés lorsque vous utilisez IIS Express.
Pour activer la prise en charge du protocole WebSocket sur Windows Server 2012 ou ultérieur :
Notes
Ces étapes ne sont pas nécessaires si vous utilisez IIS Express
Pour activer la prise en charge du protocole WebSocket sur Windows 8 ou ultérieur :
Notes
Ces étapes ne sont pas nécessaires si vous utilisez IIS Express
Si vous utilisez la prise en charge de WebSocket dans socket.io sur Node.js, désactivez le module WebSocket IIS par défaut à l’aide de l’élément webSocket
dans web.config ou applicationHost.config. Si cette étape n’est pas effectuée, le module WebSocket IIS tente de gérer la communication WebSocket, au lieu de celle de Node.js et de l’application.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
L’exemple d’application qui accompagne cet article est une application d’écho. Elle comporte une page web qui établit des connexions WebSocket, et le serveur renvoie au client les messages qu’il reçoit. L’exemple d’application prend en charge les WebSockets sur HTTP/2 lors de l’utilisation d’un framework ciblé de .NET 7 ou version ultérieure.
Exécutez l’application :
dotnet run
et accédez dans un navigateur à http://localhost:<port>
.La page web affiche l’état de connexion :
Sélectionnez Connect (Connexion) pour envoyer une requête WebSocket à l’URL affichée. Entrez un message de test, puis sélectionnez Send (Envoyer). Quand vous avez terminé, sélectionnez Close Socket (Fermer le socket). La section Communication Log (Journal des communications) signale chaque action d’ouverture, d’envoi et de fermeture en temps réel.
Cet article explique comment commencer avec les WebSockets dans ASP.NET Core. WebSocket (RFC 6455) est un protocole qui autorise des canaux de communication persistants bidirectionnels sur les connexions TCP. Son utilisation profite aux applications qui tirent parti d’une communication rapide et en temps réel, par exemple les applications de conversation, de tableau de bord et de jeu.
Affichez ou téléchargez l’exemple de code (procédure de téléchargement, procédure d’exécution).
ASP.NET Core SignalR est une bibliothèque qui simplifie l’ajout de fonctionnalités web en temps réel dans les applications. Elle utilise des WebSockets dans la mesure du possible.
Pour la plupart des applications, nous recommandons SignalR sur des WebSockets bruts. SignalR fournit un transport de secours pour les environnements où les WebSockets ne sont pas disponibles. Il fournit également un modèle d’application d’appel de procédure distante de base. De plus, dans la plupart des scénarios, SignalR ne présente aucun inconvénient majeur concernant le niveau de performances par rapport à l’utilisation de WebSockets bruts.
Pour certaines applications, gRPC sur .NET fournit une alternative aux WebSockets.
Ajoutez l’intergiciel WebSockets dans Program.cs
:
app.UseWebSockets();
Les paramètres suivants peuvent être configurés :
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Un peu plus tard dans le cycle de vie de la demande (plus loin dans Program.cs
ou dans une méthode d’action, par exemple), vérifiez s’il s’agit d’une demande WebSocket, puis acceptez-la.
L’exemple suivant est extrait d’une partie ultérieure dans 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);
}
});
Une requête WebSocket peut figurer sur toute URL, mais cet exemple de code accepte uniquement les requêtes pour /ws
.
Une approche similaire peut être adoptée dans une méthode de contrôleur :
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;
}
}
Lorsque vous utilisez un WebSocket, vous devez conserver le pipeline de l’intergiciel en cours d’exécution pendant la durée de la connexion. Si vous tentez d’envoyer ou de recevoir un message WebSocket une fois que le pipeline de l’intergiciel se termine, vous pouvez obtenir une exception comme suit :
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'.
Si vous utilisez un service en arrière-plan pour écrire des données dans un WebSocket, assurez-vous que vous conservez le pipeline de l’intergiciel en cours d’exécution. Pour ce faire, utilisez un TaskCompletionSource<TResult>. Passez le TaskCompletionSource
à l’arrière-plan de votre service et faites appeler TrySetResult lorsque vous avez terminé avec le WebSocket. Utilisez ensuite await
sur la propriété Task durant l’envoi de la requête, comme indiqué dans l’exemple suivant :
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
L’exception relative à la fermeture de WebSocket peut également se produire si vous effectuez trop tôt un retour à partir d’une méthode d’action. Si vous acceptez un socket dans une méthode d’action, attendez que le code qui l’utilise soit entièrement exécuté, avant d’effectuer un retour à partir de la méthode d’action.
N’utilisez jamais Task.Wait
, Task.Result
ou des appels de blocage similaires pour attendre la fin de l’exécution du socket, car cela peut entraîner de graves problèmes de thread. Utilisez toujours await
.
Avertissement
L’activation de la compression sur des connexions chiffrées peut exposer une application à des attaques CRIME/BREACH.
Si vous envoyez des informations sensibles, évitez d’activer la compression ou utilisez WebSocketMessageFlags.DisableCompression
lorsque vous appelez WebSocket.SendAsync
.
Cela s’applique aux deux côtés du WebSocket. Notez que l’API WebSockets dans le navigateur n’a pas de configuration pour désactiver la compression par envoi.
Si la compression des messages sur WebSockets est souhaitée, le code d’acceptation doit spécifier qu’il autorise la compression comme suit :
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits
et WebSocketAcceptContext.DisableServerContextTakeover
sont des options avancées qui contrôlent le fonctionnement de la compression.
La compression est négociée entre le client et le serveur lors de l’établissement initial d’une connexion. Vous pouvez en apprendre davantage sur la négociation dans le document RFC sur les extensions de compression pour WebSocket.
Notes
Si la négociation de compression n’est pas acceptée par le serveur ou le client, la connexion est toujours établie. Toutefois, la connexion n’utilise pas la compression lors de l’envoi et de la réception de messages.
La méthode AcceptWebSocketAsync
met à niveau la connexion TCP vers une connexion WebSocket, et fournit un objet WebSocket. Utilisez l’objet WebSocket
pour envoyer et recevoir des messages.
Le code montré précédemment qui accepte la requête WebSocket passe l’objet WebSocket
à une méthode Echo
. Le code reçoit un message et renvoie immédiatement le même message. Les messages sont envoyés et reçus dans une boucle jusqu’à ce que le client ferme la connexion :
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);
}
Quand vous acceptez le WebSocket avant de commencer cette boucle, le pipeline de middlewares se termine. À la fermeture du socket, le pipeline se déroule. Autrement dit, la requête cesse d’avancer dans le pipeline quand le WebSocket est accepté. Quand la boucle est terminée et que le socket est fermé, la requête recule dans le pipeline.
Le serveur n’est pas automatiquement informé lorsque le client se déconnecte en raison d’une perte de connectivité. Le serveur reçoit un message de déconnexion uniquement si le client l’envoie, ce qui n’est pas possible si la connexion Internet est perdue. Si vous souhaitez prendre des mesures quand cela se produit, définissez un délai d’attente lorsque rien n’est reçu du client dans un certain laps de temps.
Si le client n’envoie pas toujours de messages et que vous ne souhaitez pas appliquer le délai d’attente uniquement parce que la connexion devient inactive, demandez au client d’utiliser un minuteur pour envoyer un message ping toutes les X secondes. Sur le serveur, si un message n’arrive pas dans un délai de 2*X secondes après le précédent, mettez fin à la connexion et signalez que le client s’est déconnecté. Attendez deux fois la durée de l’intervalle de temps attendu pour autoriser les délais réseau qui peuvent retarder le message ping.
Les protections fournies par CORS ne s’appliquent pas aux WebSockets. Les navigateurs :
Access-Control
quand ils effectuent des requêtes WebSocket.Toutefois, les navigateurs envoient l’en-tête Origin
au moment de l’émission des requêtes WebSocket. Les applications doivent être configurées de manière à valider ces en-têtes, le but étant de vérifier que seuls les WebSockets provenant des origines attendues sont autorisés.
Si vous hébergez votre serveur sur « https://server.com" et votre client sur « https://client.com", ajoutez « https://client.com" à la liste AllowedOrigins à vérifier par les WebSockets.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
Notes
L’en-tête Origin
est contrôlé par le client et, comme l’en-tête Referer
, peut être falsifié. N’utilisez pas ces en-têtes comme mécanisme d’authentification.
Windows Server 2012 ou ultérieur et Windows 8 ou ultérieur avec IIS/IIS Express 8 ou ultérieure prennent en charge le protocole WebSocket.
Notes
WebSockets sont toujours activés lorsque vous utilisez IIS Express.
Pour activer la prise en charge du protocole WebSocket sur Windows Server 2012 ou ultérieur :
Notes
Ces étapes ne sont pas nécessaires si vous utilisez IIS Express
Pour activer la prise en charge du protocole WebSocket sur Windows 8 ou ultérieur :
Notes
Ces étapes ne sont pas nécessaires si vous utilisez IIS Express
Si vous utilisez la prise en charge de WebSocket dans socket.io sur Node.js, désactivez le module WebSocket IIS par défaut à l’aide de l’élément webSocket
dans web.config ou applicationHost.config. Si cette étape n’est pas effectuée, le module WebSocket IIS tente de gérer la communication WebSocket, au lieu de celle de Node.js et de l’application.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
L’exemple d’application qui accompagne cet article est une application d’écho. Elle comporte une page web qui établit des connexions WebSocket, et le serveur renvoie au client les messages qu’il reçoit. L’exemple d’application n’est pas configuré pour s’exécuter à partir de Visual Studio avec IIS Express. Par conséquent, exécutez l’application dans un interpréteur de commandes avec dotnet run
et accédez dans un navigateur à http://localhost:<port>
. La page web affiche l’état de connexion :
Sélectionnez Connect (Connexion) pour envoyer une requête WebSocket à l’URL affichée. Entrez un message de test, puis sélectionnez Send (Envoyer). Quand vous avez terminé, sélectionnez Close Socket (Fermer le socket). La section Communication Log (Journal des communications) signale chaque action d’ouverture, d’envoi et de fermeture en temps réel.
Cet article explique comment commencer avec les WebSockets dans ASP.NET Core. WebSocket (RFC 6455) est un protocole qui autorise des canaux de communication persistants bidirectionnels sur les connexions TCP. Son utilisation profite aux applications qui tirent parti d’une communication rapide et en temps réel, par exemple les applications de conversation, de tableau de bord et de jeu.
Affichez ou téléchargez un exemple de code (procédure de téléchargement). Comment exécuter.
ASP.NET Core SignalR est une bibliothèque qui simplifie l’ajout de fonctionnalités web en temps réel dans les applications. Elle utilise des WebSockets dans la mesure du possible.
Pour la plupart des applications, nous recommandons SignalR sur des WebSockets bruts. SignalR fournit un transport de secours pour les environnements où les WebSockets ne sont pas disponibles. Il fournit également un modèle d’application d’appel de procédure distante de base. De plus, dans la plupart des scénarios, SignalR ne présente aucun inconvénient majeur concernant le niveau de performances par rapport à l’utilisation de WebSockets bruts.
Pour certaines applications, gRPC sur .NET fournit une alternative aux WebSockets.
Ajoutez le middleware WebSocket dans la méthode Configure
de la classe Startup
:
app.UseWebSockets();
Notes
Si vous souhaitez accepter les demandes WebSocket dans un contrôleur, l’appel à app.UseWebSockets
doit avoir lieu avant app.UseEndpoints
.
Les paramètres suivants peuvent être configurés :
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
app.UseWebSockets(webSocketOptions);
Un peu plus tard dans le cycle de vie de la requête (plus loin dans la méthode Configure
ou dans une méthode action, par exemple), vérifiez s’il s’agit d’une requête WebSocket, puis acceptez-la.
L’exemple suivant est extrait d’une partie ultérieure de la méthode 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();
}
});
Une requête WebSocket peut figurer sur toute URL, mais cet exemple de code accepte uniquement les requêtes pour /ws
.
Une approche similaire peut être adoptée dans une méthode de contrôleur :
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;
}
}
Lorsque vous utilisez un WebSocket, vous devez conserver le pipeline de l’intergiciel en cours d’exécution pendant la durée de la connexion. Si vous tentez d’envoyer ou de recevoir un message WebSocket une fois que le pipeline de l’intergiciel se termine, vous pouvez obtenir une exception comme suit :
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'.
Si vous utilisez un service en arrière-plan pour écrire des données dans un WebSocket, assurez-vous que vous conservez le pipeline de l’intergiciel en cours d’exécution. Pour ce faire, utilisez un TaskCompletionSource<TResult>. Passez le TaskCompletionSource
à l’arrière-plan de votre service et faites appeler TrySetResult lorsque vous avez terminé avec le WebSocket. Utilisez ensuite await
sur la propriété Task durant l’envoi de la requête, comme indiqué dans l’exemple suivant :
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’exception relative à la fermeture de WebSocket peut également se produire si vous effectuez trop tôt un retour à partir d’une méthode d’action. Si vous acceptez un socket dans une méthode d’action, attendez que le code qui l’utilise soit entièrement exécuté, avant d’effectuer un retour à partir de la méthode d’action.
N’utilisez jamais Task.Wait
, Task.Result
ou des appels de blocage similaires pour attendre la fin de l’exécution du socket, car cela peut entraîner de graves problèmes de thread. Utilisez toujours await
.
La méthode AcceptWebSocketAsync
met à niveau la connexion TCP vers une connexion WebSocket, et fournit un objet WebSocket. Utilisez l’objet WebSocket
pour envoyer et recevoir des messages.
Le code montré précédemment qui accepte la requête WebSocket passe l’objet WebSocket
à une méthode Echo
. Le code reçoit un message et renvoie immédiatement le même message. Les messages sont envoyés et reçus dans une boucle jusqu’à ce que le client ferme la connexion :
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);
}
Quand vous acceptez le WebSocket avant de commencer cette boucle, le pipeline de middlewares se termine. À la fermeture du socket, le pipeline se déroule. Autrement dit, la requête cesse d’avancer dans le pipeline quand le WebSocket est accepté. Quand la boucle est terminée et que le socket est fermé, la requête recule dans le pipeline.
Le serveur n’est pas automatiquement informé lorsque le client se déconnecte en raison d’une perte de connectivité. Le serveur reçoit un message de déconnexion uniquement si le client l’envoie, ce qui n’est pas possible si la connexion Internet est perdue. Si vous souhaitez prendre des mesures quand cela se produit, définissez un délai d’attente lorsque rien n’est reçu du client dans un certain laps de temps.
Si le client n’envoie pas toujours de messages et que vous ne souhaitez pas appliquer le délai d’attente uniquement parce que la connexion devient inactive, demandez au client d’utiliser un minuteur pour envoyer un message ping toutes les X secondes. Sur le serveur, si un message n’arrive pas dans un délai de 2*X secondes après le précédent, mettez fin à la connexion et signalez que le client s’est déconnecté. Attendez deux fois la durée de l’intervalle de temps attendu pour autoriser les délais réseau qui peuvent retarder le message ping.
Notes
Le système interne ManagedWebSocket
gère implicitement les trames Ping/Pong pour maintenir la connexion en vie si l’option KeepAliveInterval
est supérieure à zéro, ce qui correspond par défaut à 30 secondes (TimeSpan.FromSeconds(30)
).
Les protections fournies par CORS ne s’appliquent pas aux WebSockets. Les navigateurs :
Access-Control
quand ils effectuent des requêtes WebSocket.Toutefois, les navigateurs envoient l’en-tête Origin
au moment de l’émission des requêtes WebSocket. Les applications doivent être configurées de manière à valider ces en-têtes, le but étant de vérifier que seuls les WebSockets provenant des origines attendues sont autorisés.
Si vous hébergez votre serveur sur « https://server.com" et votre client sur « https://client.com", ajoutez « https://client.com" à la liste AllowedOrigins à vérifier par les WebSockets.
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
webSocketOptions.AllowedOrigins.Add("https://client.com");
webSocketOptions.AllowedOrigins.Add("https://www.client.com");
app.UseWebSockets(webSocketOptions);
Notes
L’en-tête Origin
est contrôlé par le client et, comme l’en-tête Referer
, peut être falsifié. N’utilisez pas ces en-têtes comme mécanisme d’authentification.
Windows Server 2012 ou ultérieur et Windows 8 ou ultérieur avec IIS/IIS Express 8 ou ultérieure prennent en charge le protocole WebSocket.
Notes
WebSockets sont toujours activés lorsque vous utilisez IIS Express.
Pour activer la prise en charge du protocole WebSocket sur Windows Server 2012 ou ultérieur :
Notes
Ces étapes ne sont pas nécessaires si vous utilisez IIS Express
Pour activer la prise en charge du protocole WebSocket sur Windows 8 ou ultérieur :
Notes
Ces étapes ne sont pas nécessaires si vous utilisez IIS Express
Si vous utilisez la prise en charge de WebSocket dans socket.io sur Node.js, désactivez le module WebSocket IIS par défaut à l’aide de l’élément webSocket
dans web.config ou applicationHost.config. Si cette étape n’est pas effectuée, le module WebSocket IIS tente de gérer la communication WebSocket, au lieu de celle de Node.js et de l’application.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
L’exemple d’application qui accompagne cet article est une application d’écho. Elle comporte une page web qui établit des connexions WebSocket, et le serveur renvoie au client les messages qu’il reçoit. L’exemple d’application n’est pas configuré pour s’exécuter à partir de Visual Studio avec IIS Express. Par conséquent, exécutez l’application dans un interpréteur de commandes avec dotnet run
et accédez dans un navigateur à http://localhost:5000
. La page web affiche l’état de connexion :
Sélectionnez Connect (Connexion) pour envoyer une requête WebSocket à l’URL affichée. Entrez un message de test, puis sélectionnez Send (Envoyer). Quand vous avez terminé, sélectionnez Close Socket (Fermer le socket). La section Communication Log (Journal des communications) signale chaque action d’ouverture, d’envoi et de fermeture en temps réel.
Commentaires sur ASP.NET Core
ASP.NET Core est un projet open source. Sélectionnez un lien pour fournir des commentaires :
Événements
19 nov., 23 h - 21 nov., 23 h
Participez à des sessions en ligne à Microsoft Ignite créées pour développer vos compétences et vous aider à résoudre les problèmes complexes d’aujourd’hui.
S’inscrire maintenantEntrainement
Module
Introduction à ASP.NET Core SignalR - Training
Dans ce module d’introduction à ASP.NET Core SignalR, vous allez découvrir ce qu’est SignalR, comment il fonctionne et quand l’utiliser.