Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Note
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Warning
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, consulte a Política de Suporte do .NET e do .NET Core. Para a versão atual, consulte a versão do .NET 10 deste artigo.
Este artigo explica como começar a usar o WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais em conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como chat, painel e aplicativos de jogos.
Exibir ou fazer download do código de exemplo (como fazer download, como executar).
Suporte a WebSockets HTTP/2
O uso de WebSockets por HTTP/2 aproveita novos recursos, como:
- Compressão de cabeçalho.
- Multiplexação, que reduz o tempo e os recursos necessários ao fazer várias solicitações ao servidor.
Esses recursos com suporte estão disponíveis no Kestrel em todas as plataformas habilitadas para HTTP/2. A negociação de versão é automática em navegadores e no Kestrel, ou seja, nenhuma nova API é necessária.
O .NET 7 introduziu suporte a WebSockets por HTTP/2 para Kestrel, o cliente JavaScript SignalR, e SignalR com Blazor WebAssembly.
Note
WebSockets HTTP/2 usam solicitações CONNECT em vez de GET, portanto, suas próprias rotas e controladores podem precisar de atualização. Para obter mais informações, consulte Adicionar suporte a WebSockets HTTP/2 para controladores existentes neste artigo.
Chrome, Edge e Firefox (versão 128 e posterior) têm WebSockets HTTP/2 habilitados por padrão. Você pode verificar ou alterar essa configuração no Firefox abrindo about:config e localizando a network.http.http2.websockets preferência.
WebSockets foram originalmente projetados para HTTP/1.1, mas desde então foram adaptados para funcionar via HTTP/2. (RFC 8441)
SignalR
ASP.NET Core SignalR é uma biblioteca que simplifica a adição da funcionalidade da Web em tempo real aos aplicativos. Ele usa WebSockets sempre que possível.
Para a maioria dos aplicativos, recomendamos o SignalR ao invés de WebSockets brutos. SignalR:
- Fornece um transporte alternativo para ambientes em que WebSockets não estão disponíveis.
- Fornece um modelo básico de aplicativo de chamada de procedimento remoto.
- Não tem nenhuma desvantagem de desempenho significativa em comparação ao uso de WebSockets brutos, na maioria dos cenários.
Há suporte para WebSockets via HTTP/2 para:
- ASP.NET Core SignalR Cliente JavaScript
- ASP.NET Core SignalR com Blazor WebAssembly
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa para WebSockets.
Prerequisites
- Qualquer sistema operacional compatível com o ASP.NET Core:
- Windows 7/Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo é executado no Windows com o IIS:
- Windows 8/Windows Server 2012 ou posterior
- IIS 8/IIS 8 Express
- WebSockets devem estar habilitados. Consulte a seção Suporte do IIS/IIS Express.
- Se o aplicativo é executado no HTTP.sys:
- Windows 8/Windows Server 2012 ou posterior
- Para saber quais são os navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware de WebSockets em Program.cs:
app.UseWebSockets();
As seguintes configurações podem ser definidas:
- KeepAliveInterval - Com que frequência enviar quadros de "ping" ao cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
-
KeepAliveTimeout - Quanto tempo esperar por um quadro "pong" do cliente após o envio de um quadro "ping". Se o tempo limite for excedido, a conexão WebSocket será anulada e
WebSocket.ReceiveAsyncgerará uma exceção. O padrão éTimeout.InfiniteTimeSpan(desabilitado). Para obter mais informações, consulte Lidar com desconexões de cliente neste artigo. - AllowedOrigins – Uma lista de valores de cabeçalho de origem permitidos para solicitações do WebSocket. Por padrão, todas as origens são permitidas. Para obter mais informações, consulte Restrição de origem do WebSocket neste artigo.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Aceitar solicitações do WebSocket
Futuramente no ciclo de vida da solicitação (mais tarde em Program.cs ou em um método de ação, por exemplo), verifique se é uma solicitação do WebSocket e aceite-a.
O exemplo a seguir é de mais adiante em 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);
}
});
Uma solicitação do WebSocket pode entrar em qualquer URL, mas esse código de exemplo aceita apenas solicitações de /ws.
Uma abordagem semelhante pode ser adotada em um método de controlador:
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;
}
}
Ao usar um WebSocket, você deve manter o pipeline de middleware em execução durante a conexão. Se tentar enviar ou receber uma mensagem do WebSocket após o término do pipeline de middleware, você poderá receber uma exceção semelhante à seguinte:
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 você estiver usando um serviço em segundo plano para gravar dados em um WebSocket, mantenha o pipeline do middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e faça-o chamar TrySetResult quando terminar com o WebSocket. Em seguida await a propriedade Task durante a solicitação, conforme é mostrado no exemplo a seguir:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
A exceção fechada do WebSocket também poderá ocorrer ao retornar muito rapidamente de um método de ação. Ao aceitar um soquete de um método de ação, aguarde até que o código que usa o soquete seja concluído antes de retornar do método de ação.
Nunca use Task.Wait, Task.Result ou chamadas de bloqueio semelhantes para aguardar a conclusão do soquete, pois isso pode causar sérios problemas de threading. Sempre use await.
Adicionar suporte a WebSockets HTTP/2 para controladores existentes
O .NET 7 introduziu suporte a WebSockets por HTTP/2 para Kestrel, o cliente JavaScript SignalR, e SignalR com Blazor WebAssembly. WebSockets HTTP/2 usam solicitações CONNECT em vez de GET. Se você usou [HttpGet("/path")] anteriormente em seu método de ação do controlador para solicitações Websocket, atualize-o para usar [Route("/path")] em vez disso.
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;
}
}
Compression
Warning
Habilitar a compactação em conexões criptografadas pode tornar um aplicativo sujeito a ataques CRIME/BREACH.
Se estiver enviando informações confidenciais, evite habilitar a compactação ou use WebSocketMessageFlags.DisableCompression ao chamar WebSocket.SendAsync.
Isso se aplica a ambos os lados do WebSocket. Observe que a API dos WebSockets no navegador não tem configuração para desabilitar a compactação por envio.
Caso deseje fazer a compactação de mensagens por WebSockets, o código de aceitação deverá especificar que ele permite a compactação da seguinte maneira:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits e WebSocketAcceptContext.DisableServerContextTakeover são opções avançadas que controlam como a compactação funciona.
A compactação é negociada entre o cliente e o servidor ao estabelecer uma conexão pela primeira vez. Você pode ler mais sobre a negociação na RFC sobre Extensões de Compactação para WebSocket.
Note
Se a negociação de compactação não for aceita pelo servidor ou pelo cliente, a conexão ainda será estabelecida. No entanto, a conexão não usa compactação ao enviar e receber mensagens.
Enviar e receber mensagens
O método AcceptWebSocketAsync atualiza a conexão TCP para uma conexão WebSocket e fornece um objeto WebSocket. Use o objeto WebSocket para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação do WebSocket passa o objeto WebSocket para um método Echo. O código recebe uma mensagem e envia de volta imediatamente a mesma mensagem. As mensagens são enviadas e recebidas em um loop até que o cliente feche a conexão:
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);
}
Ao aceitar a conexão WebSocket antes de iniciar o loop, o pipeline de middleware é encerrado. Ao fechar o soquete, o pipeline é desenrolado. Ou seja, a solicitação deixa de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação continua a avançar no pipeline.
Lidar com desconexões de clientes
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas se o cliente a enviar, o que não pode ser feito se a conexão com a internet for perdida. Se quiser tomar alguma medida quando isso ocorrer, defina um tempo limite, caso não receba nada do cliente após uma determinada janela de tempo.
Se o cliente nem sempre estiver enviando mensagens e você não quiser que ocorra um tempo limite apenas porque a conexão ficou ociosa, faça o cliente usar um temporizador para enviar uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não chegar dentro de 2*X segundos após a mensagem anterior, encerre a conexão e informe que o cliente se desconectou. Aguarde o dobro do intervalo de tempo esperado a fim de deixar um tempo extra para atrasos na rede, que possam atrasar a mensagem de ping.
Tempo limite de keep-alive
O middleware WebSockets pode ser configurado com um tempo limite de keep-alive. O tempo limite de keep-alive aborta a conexão WebSocket e lança uma exceção de WebSocket.ReceiveAsync se ambas as seguintes condições forem atendidas:
- O servidor envia um quadro de ping usando o protocolo WebSocket.
- O cliente não responde com um quadro pong dentro do tempo limite especificado.
O servidor envia automaticamente o quadro de ping e configura sua frequência com KeepAliveInterval.
A configuração de tempo limite de keep-alive é útil para detectar conexões que podem ser lentas ou desconectar-se abruptamente. O padrão é Timeout.InfiniteTimeSpan, o que desabilita o tempo limite.
O tempo limite de keep-alive pode ser configurado globalmente para o middleware WebSocket:
app.UseWebSockets(new WebSocketOptions { KeepAliveTimeout = TimeSpan.FromSeconds(15) });
Ou configurado para cada WebSocket aceito:
using var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { KeepAliveTimeout = TimeSpan.FromSeconds(15) });
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam ao WebSockets. Navegadores não:
- Executar solicitações de preflight do CORS.
- Respeite as restrições especificadas nos cabeçalhos
Access-Controlao fazer solicitações WebSocket.
No entanto, os navegadores enviam o cabeçalho Origin ao emitir solicitações de WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos e garantir que apenas WebSockets provenientes de origens esperadas sejam permitidos.
Se você estiver hospedando o servidor em "https://server.com" e hospedando seu cliente em "https://client.com", adicione "https://client.com" à lista AllowedOrigins para que seja feita a verificação pelos 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);
Note
O cabeçalho Origin é controlado pelo cliente e, como o cabeçalho Referer, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte ao IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com o IIS/IIS Express 8 ou posterior são compatíveis com o protocolo WebSocket, mas não para WebSockets via HTTP/2.
Note
WebSockets estão sempre habilitados ao usar o IIS Express.
Habilitar WebSockets no IIS
Para habilitar o suporte para o protocolo WebSocket no Windows Server 2012 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Use o assistente Adicionar Funções e Recursos por meio do menu Gerenciar ou do link no Gerenciador do Servidor.
- Selecione Instalação baseada em função ou em recurso. Selecione Próximo.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Próximo.
- Expanda Servidor Web (IIS) na árvore Funções, expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione o Protocolo WebSocket. Selecione Próximo.
- Se não forem necessários recursos adicionais, selecione Avançar.
- Selecione Instalar.
- Quando a instalação for concluída, selecione Fechar para sair do assistente.
Para habilitar o suporte para o protocolo WebSocket no Windows 8 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Navegue até Painel de Controle>Programas>Programas e Recursos>Ativar ou desativar recursos do Windows (lado esquerdo da tela).
- Abra os nós a seguir: Serviços de Informações da Internet>Serviços da World Wide Web>Recursos de Desenvolvimento de Aplicativos.
- Selecione o recurso Protocolo WebSocket. Selecione OK.
Desabilitar WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte do WebSocket em socket.io no Node.js, desabilite o módulo WebSocket do IIS padrão usando o elemento webSocket em web.config ou applicationHost.config. Se essa etapa não for executada, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez de Node.js e o aplicativo.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Aplicativo de exemplo
O aplicativo de exemplo que acompanha este artigo é um aplicativo de eco. Ele tem uma página da Web que faz conexões do WebSocket e o servidor reenvia para o cliente todas as mensagens recebidas. O aplicativo de amostra dá suporte a WebSockets via HTTP/2 ao usar uma estrutura de destino do .NET 7 ou posterior.
Executar o aplicativo:
- Para executar o aplicativo no Visual Studio: abra o projeto de amostra no Visual Studio e pressione Ctrl+F5 para ser executado sem o depurador.
- Para executar o aplicativo em um shell de comando: execute o comando
dotnet rune navegue em um navegador atéhttp://localhost:<port>.
A página da Web mostra o status de conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL exibida. Insira uma mensagem de teste e selecione Enviar. Quando terminar, selecione Fechar Soquete. A seção Log de Comunicação relata cada ação de abertura, envio e fechamento conforme ela ocorre.
Este artigo explica como começar a usar o WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais em conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como chat, painel e aplicativos de jogos.
Exibir ou fazer download do código de exemplo (como fazer download, como executar).
Suporte a WebSockets HTTP/2
O uso de WebSockets por HTTP/2 aproveita novos recursos, como:
- Compressão de cabeçalho.
- Multiplexação, que reduz o tempo e os recursos necessários ao fazer várias solicitações ao servidor.
Esses recursos com suporte estão disponíveis no Kestrel em todas as plataformas habilitadas para HTTP/2. A negociação de versão é automática em navegadores e no Kestrel, ou seja, nenhuma nova API é necessária.
O .NET 7 introduziu suporte a WebSockets por HTTP/2 para Kestrel, o cliente JavaScript SignalR, e SignalR com Blazor WebAssembly.
Note
WebSockets HTTP/2 usam solicitações CONNECT em vez de GET, portanto, suas próprias rotas e controladores podem precisar de atualização. Para obter mais informações, consulte Adicionar suporte a WebSockets HTTP/2 para controladores existentes neste artigo.
Chrome, Edge e Firefox (versão 128 e posterior) têm WebSockets HTTP/2 habilitados por padrão. Você pode verificar ou alterar essa configuração no Firefox abrindo about:config e localizando a network.http.http2.websockets preferência.
WebSockets foram originalmente projetados para HTTP/1.1, mas desde então foram adaptados para funcionar via HTTP/2. (RFC 8441)
SignalR
ASP.NET Core SignalR é uma biblioteca que simplifica a adição da funcionalidade da Web em tempo real aos aplicativos. Ele usa WebSockets sempre que possível.
Para a maioria dos aplicativos, recomendamos o SignalR ao invés de WebSockets brutos. SignalR:
- Fornece um transporte alternativo para ambientes em que WebSockets não estão disponíveis.
- Fornece um modelo básico de aplicativo de chamada de procedimento remoto.
- Não tem nenhuma desvantagem de desempenho significativa em comparação ao uso de WebSockets brutos, na maioria dos cenários.
Há suporte para WebSockets via HTTP/2 para:
- ASP.NET Core SignalR Cliente JavaScript
- ASP.NET Core SignalR com Blazor WebAssembly
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa para WebSockets.
Prerequisites
- Qualquer sistema operacional compatível com o ASP.NET Core:
- Windows 7/Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo é executado no Windows com o IIS:
- Windows 8/Windows Server 2012 ou posterior
- IIS 8/IIS 8 Express
- WebSockets devem estar habilitados. Consulte a seção Suporte do IIS/IIS Express.
- Se o aplicativo é executado no HTTP.sys:
- Windows 8/Windows Server 2012 ou posterior
- Para saber quais são os navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware de WebSockets em Program.cs:
app.UseWebSockets();
As seguintes configurações podem ser definidas:
- KeepAliveInterval - Com que frequência enviar quadros de "ping" ao cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins – Uma lista de valores de cabeçalho de origem permitidos para solicitações do WebSocket. Por padrão, todas as origens são permitidas. Para obter mais informações, consulte Restrição de origem do WebSocket neste artigo.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Aceitar solicitações do WebSocket
Futuramente no ciclo de vida da solicitação (mais tarde em Program.cs ou em um método de ação, por exemplo), verifique se é uma solicitação do WebSocket e aceite-a.
O exemplo a seguir é de mais adiante em 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);
}
});
Uma solicitação do WebSocket pode entrar em qualquer URL, mas esse código de exemplo aceita apenas solicitações de /ws.
Uma abordagem semelhante pode ser adotada em um método de controlador:
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;
}
}
Ao usar um WebSocket, você deve manter o pipeline de middleware em execução durante a conexão. Se tentar enviar ou receber uma mensagem do WebSocket após o término do pipeline de middleware, você poderá receber uma exceção semelhante à seguinte:
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 você estiver usando um serviço em segundo plano para gravar dados em um WebSocket, mantenha o pipeline do middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e faça-o chamar TrySetResult quando terminar com o WebSocket. Em seguida await a propriedade Task durante a solicitação, conforme é mostrado no exemplo a seguir:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
A exceção fechada do WebSocket também poderá ocorrer ao retornar muito rapidamente de um método de ação. Ao aceitar um soquete de um método de ação, aguarde até que o código que usa o soquete seja concluído antes de retornar do método de ação.
Nunca use Task.Wait, Task.Result ou chamadas de bloqueio semelhantes para aguardar a conclusão do soquete, pois isso pode causar sérios problemas de threading. Sempre use await.
Adicionar suporte a WebSockets HTTP/2 para controladores existentes
O .NET 7 introduziu suporte a WebSockets por HTTP/2 para Kestrel, o cliente JavaScript SignalR, e SignalR com Blazor WebAssembly. WebSockets HTTP/2 usam solicitações CONNECT em vez de GET. Se você usou [HttpGet("/path")] anteriormente em seu método de ação do controlador para solicitações Websocket, atualize-o para usar [Route("/path")] em vez disso.
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;
}
}
Compression
Warning
Habilitar a compactação em conexões criptografadas pode tornar um aplicativo sujeito a ataques CRIME/BREACH.
Se estiver enviando informações confidenciais, evite habilitar a compactação ou use WebSocketMessageFlags.DisableCompression ao chamar WebSocket.SendAsync.
Isso se aplica a ambos os lados do WebSocket. Observe que a API dos WebSockets no navegador não tem configuração para desabilitar a compactação por envio.
Caso deseje fazer a compactação de mensagens por WebSockets, o código de aceitação deverá especificar que ele permite a compactação da seguinte maneira:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits e WebSocketAcceptContext.DisableServerContextTakeover são opções avançadas que controlam como a compactação funciona.
A compactação é negociada entre o cliente e o servidor ao estabelecer uma conexão pela primeira vez. Você pode ler mais sobre a negociação na RFC sobre Extensões de Compactação para WebSocket.
Note
Se a negociação de compactação não for aceita pelo servidor ou pelo cliente, a conexão ainda será estabelecida. No entanto, a conexão não usa compactação ao enviar e receber mensagens.
Enviar e receber mensagens
O método AcceptWebSocketAsync atualiza a conexão TCP para uma conexão WebSocket e fornece um objeto WebSocket. Use o objeto WebSocket para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação do WebSocket passa o objeto WebSocket para um método Echo. O código recebe uma mensagem e envia de volta imediatamente a mesma mensagem. As mensagens são enviadas e recebidas em um loop até que o cliente feche a conexão:
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);
}
Ao aceitar a conexão WebSocket antes de iniciar o loop, o pipeline de middleware é encerrado. Ao fechar o soquete, o pipeline é desenrolado. Ou seja, a solicitação deixa de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação continua a avançar no pipeline.
Lidar com desconexões de clientes
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas se o cliente a enviar, o que não pode ser feito se a conexão com a internet for perdida. Se quiser tomar alguma medida quando isso ocorrer, defina um tempo limite, caso não receba nada do cliente após uma determinada janela de tempo.
Se o cliente nem sempre estiver enviando mensagens e você não quiser que ocorra um tempo limite apenas porque a conexão ficou ociosa, faça o cliente usar um temporizador para enviar uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não chegar dentro de 2*X segundos após a mensagem anterior, encerre a conexão e informe que o cliente se desconectou. Aguarde o dobro do intervalo de tempo esperado a fim de deixar um tempo extra para atrasos na rede, que possam atrasar a mensagem de ping.
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam ao WebSockets. Navegadores não:
- Executar solicitações de preflight do CORS.
- Respeite as restrições especificadas nos cabeçalhos
Access-Controlao fazer solicitações WebSocket.
No entanto, os navegadores enviam o cabeçalho Origin ao emitir solicitações de WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos e garantir que apenas WebSockets provenientes de origens esperadas sejam permitidos.
Se você estiver hospedando o servidor em "https://server.com" e hospedando seu cliente em "https://client.com", adicione "https://client.com" à lista AllowedOrigins para que seja feita a verificação pelos 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);
Note
O cabeçalho Origin é controlado pelo cliente e, como o cabeçalho Referer, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte ao IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com o IIS/IIS Express 8 ou posterior são compatíveis com o protocolo WebSocket, mas não para WebSockets via HTTP/2.
Note
WebSockets estão sempre habilitados ao usar o IIS Express.
Habilitar WebSockets no IIS
Para habilitar o suporte para o protocolo WebSocket no Windows Server 2012 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Use o assistente Adicionar Funções e Recursos por meio do menu Gerenciar ou do link no Gerenciador do Servidor.
- Selecione Instalação baseada em função ou em recurso. Selecione Próximo.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Próximo.
- Expanda Servidor Web (IIS) na árvore Funções, expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione o Protocolo WebSocket. Selecione Próximo.
- Se não forem necessários recursos adicionais, selecione Avançar.
- Selecione Instalar.
- Quando a instalação for concluída, selecione Fechar para sair do assistente.
Para habilitar o suporte para o protocolo WebSocket no Windows 8 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Navegue até Painel de Controle>Programas>Programas e Recursos>Ativar ou desativar recursos do Windows (lado esquerdo da tela).
- Abra os nós a seguir: Serviços de Informações da Internet>Serviços da World Wide Web>Recursos de Desenvolvimento de Aplicativos.
- Selecione o recurso Protocolo WebSocket. Selecione OK.
Desabilitar WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte do WebSocket em socket.io no Node.js, desabilite o módulo WebSocket do IIS padrão usando o elemento webSocket em web.config ou applicationHost.config. Se essa etapa não for executada, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez de Node.js e o aplicativo.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Aplicativo de exemplo
O aplicativo de exemplo que acompanha este artigo é um aplicativo de eco. Ele tem uma página da Web que faz conexões do WebSocket e o servidor reenvia para o cliente todas as mensagens recebidas. O aplicativo de amostra dá suporte a WebSockets via HTTP/2 ao usar uma estrutura de destino do .NET 7 ou posterior.
Executar o aplicativo:
- Para executar o aplicativo no Visual Studio: abra o projeto de amostra no Visual Studio e pressione Ctrl+F5 para ser executado sem o depurador.
- Para executar o aplicativo em um shell de comando: execute o comando
dotnet rune navegue em um navegador atéhttp://localhost:<port>.
A página da Web mostra o status de conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL exibida. Insira uma mensagem de teste e selecione Enviar. Quando terminar, selecione Fechar Soquete. A seção Log de Comunicação relata cada ação de abertura, envio e fechamento conforme ela ocorre.
Este artigo explica como começar a usar o WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais em conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como chat, painel e aplicativos de jogos.
Exibir ou fazer download do código de exemplo (como fazer download, como executar).
Suporte a WebSockets HTTP/2
O uso de WebSockets por HTTP/2 aproveita novos recursos, como:
- Compressão de cabeçalho.
- Multiplexação, que reduz o tempo e os recursos necessários ao fazer várias solicitações ao servidor.
Esses recursos com suporte estão disponíveis no Kestrel em todas as plataformas habilitadas para HTTP/2. A negociação de versão é automática em navegadores e no Kestrel, ou seja, nenhuma nova API é necessária.
O .NET 7 introduziu suporte a WebSockets por HTTP/2 para Kestrel, o cliente JavaScript SignalR, e SignalR com Blazor WebAssembly.
Note
WebSockets HTTP/2 usam solicitações CONNECT em vez de GET, portanto, suas próprias rotas e controladores podem precisar de atualização. Para obter mais informações, consulte Adicionar suporte a WebSockets HTTP/2 para controladores existentes neste artigo.
Chrome, Edge e Firefox (versão 128 e posterior) têm WebSockets HTTP/2 habilitados por padrão. Você pode verificar ou alterar essa configuração no Firefox abrindo about:config e localizando a network.http.http2.websockets preferência.
WebSockets foram originalmente projetados para HTTP/1.1, mas desde então foram adaptados para funcionar via HTTP/2. (RFC 8441)
SignalR
ASP.NET Core SignalR é uma biblioteca que simplifica a adição da funcionalidade da Web em tempo real aos aplicativos. Ele usa WebSockets sempre que possível.
Para a maioria dos aplicativos, recomendamos o SignalR ao invés de WebSockets brutos. SignalR:
- Fornece um transporte alternativo para ambientes em que WebSockets não estão disponíveis.
- Fornece um modelo básico de aplicativo de chamada de procedimento remoto.
- Não tem nenhuma desvantagem de desempenho significativa em comparação ao uso de WebSockets brutos, na maioria dos cenários.
Há suporte para WebSockets via HTTP/2 para:
- ASP.NET Core SignalR Cliente JavaScript
- ASP.NET Core SignalR com Blazor WebAssembly
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa para WebSockets.
Prerequisites
- Qualquer sistema operacional compatível com o ASP.NET Core:
- Windows 7/Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo é executado no Windows com o IIS:
- Windows 8/Windows Server 2012 ou posterior
- IIS 8/IIS 8 Express
- WebSockets devem estar habilitados. Consulte a seção Suporte do IIS/IIS Express.
- Se o aplicativo é executado no HTTP.sys:
- Windows 8/Windows Server 2012 ou posterior
- Para saber quais são os navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware de WebSockets em Program.cs:
app.UseWebSockets();
As seguintes configurações podem ser definidas:
- KeepAliveInterval - Com que frequência enviar quadros de "ping" ao cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins – Uma lista de valores de cabeçalho de origem permitidos para solicitações do WebSocket. Por padrão, todas as origens são permitidas. Para obter mais informações, consulte Restrição de origem do WebSocket neste artigo.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Aceitar solicitações do WebSocket
Futuramente no ciclo de vida da solicitação (mais tarde em Program.cs ou em um método de ação, por exemplo), verifique se é uma solicitação do WebSocket e aceite-a.
O exemplo a seguir é de mais adiante em 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);
}
});
Uma solicitação do WebSocket pode entrar em qualquer URL, mas esse código de exemplo aceita apenas solicitações de /ws.
Uma abordagem semelhante pode ser adotada em um método de controlador:
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;
}
}
Ao usar um WebSocket, você deve manter o pipeline de middleware em execução durante a conexão. Se tentar enviar ou receber uma mensagem do WebSocket após o término do pipeline de middleware, você poderá receber uma exceção semelhante à seguinte:
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 você estiver usando um serviço em segundo plano para gravar dados em um WebSocket, mantenha o pipeline do middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e faça-o chamar TrySetResult quando terminar com o WebSocket. Em seguida await a propriedade Task durante a solicitação, conforme é mostrado no exemplo a seguir:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
A exceção fechada do WebSocket também poderá ocorrer ao retornar muito rapidamente de um método de ação. Ao aceitar um soquete de um método de ação, aguarde até que o código que usa o soquete seja concluído antes de retornar do método de ação.
Nunca use Task.Wait, Task.Result ou chamadas de bloqueio semelhantes para aguardar a conclusão do soquete, pois isso pode causar sérios problemas de threading. Sempre use await.
Adicionar suporte a WebSockets HTTP/2 para controladores existentes
O .NET 7 introduziu suporte a WebSockets por HTTP/2 para Kestrel, o cliente JavaScript SignalR, e SignalR com Blazor WebAssembly. WebSockets HTTP/2 usam solicitações CONNECT em vez de GET. Se você usou [HttpGet("/path")] anteriormente em seu método de ação do controlador para solicitações Websocket, atualize-o para usar [Route("/path")] em vez disso.
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;
}
}
Compression
Warning
Habilitar a compactação em conexões criptografadas pode tornar um aplicativo sujeito a ataques CRIME/BREACH.
Se estiver enviando informações confidenciais, evite habilitar a compactação ou use WebSocketMessageFlags.DisableCompression ao chamar WebSocket.SendAsync.
Isso se aplica a ambos os lados do WebSocket. Observe que a API dos WebSockets no navegador não tem configuração para desabilitar a compactação por envio.
Caso deseje fazer a compactação de mensagens por WebSockets, o código de aceitação deverá especificar que ele permite a compactação da seguinte maneira:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits e WebSocketAcceptContext.DisableServerContextTakeover são opções avançadas que controlam como a compactação funciona.
A compactação é negociada entre o cliente e o servidor ao estabelecer uma conexão pela primeira vez. Você pode ler mais sobre a negociação na RFC sobre Extensões de Compactação para WebSocket.
Note
Se a negociação de compactação não for aceita pelo servidor ou pelo cliente, a conexão ainda será estabelecida. No entanto, a conexão não usa compactação ao enviar e receber mensagens.
Enviar e receber mensagens
O método AcceptWebSocketAsync atualiza a conexão TCP para uma conexão WebSocket e fornece um objeto WebSocket. Use o objeto WebSocket para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação do WebSocket passa o objeto WebSocket para um método Echo. O código recebe uma mensagem e envia de volta imediatamente a mesma mensagem. As mensagens são enviadas e recebidas em um loop até que o cliente feche a conexão:
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);
}
Ao aceitar a conexão WebSocket antes de iniciar o loop, o pipeline de middleware é encerrado. Ao fechar o soquete, o pipeline é desenrolado. Ou seja, a solicitação deixa de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação continua a avançar no pipeline.
Lidar com desconexões de clientes
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas se o cliente a enviar, o que não pode ser feito se a conexão com a internet for perdida. Se quiser tomar alguma medida quando isso ocorrer, defina um tempo limite, caso não receba nada do cliente após uma determinada janela de tempo.
Se o cliente nem sempre estiver enviando mensagens e você não quiser que ocorra um tempo limite apenas porque a conexão ficou ociosa, faça o cliente usar um temporizador para enviar uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não chegar dentro de 2*X segundos após a mensagem anterior, encerre a conexão e informe que o cliente se desconectou. Aguarde o dobro do intervalo de tempo esperado a fim de deixar um tempo extra para atrasos na rede, que possam atrasar a mensagem de ping.
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam ao WebSockets. Navegadores não:
- Executar solicitações de preflight do CORS.
- Respeite as restrições especificadas nos cabeçalhos
Access-Controlao fazer solicitações WebSocket.
No entanto, os navegadores enviam o cabeçalho Origin ao emitir solicitações de WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos e garantir que apenas WebSockets provenientes de origens esperadas sejam permitidos.
Se você estiver hospedando o servidor em "https://server.com" e hospedando seu cliente em "https://client.com", adicione "https://client.com" à lista AllowedOrigins para que seja feita a verificação pelos 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);
Note
O cabeçalho Origin é controlado pelo cliente e, como o cabeçalho Referer, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte ao IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com o IIS/IIS Express 8 ou posterior são compatíveis com o protocolo WebSocket, mas não para WebSockets via HTTP/2.
Note
WebSockets estão sempre habilitados ao usar o IIS Express.
Habilitar WebSockets no IIS
Para habilitar o suporte para o protocolo WebSocket no Windows Server 2012 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Use o assistente Adicionar Funções e Recursos por meio do menu Gerenciar ou do link no Gerenciador do Servidor.
- Selecione Instalação baseada em função ou em recurso. Selecione Próximo.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Próximo.
- Expanda Servidor Web (IIS) na árvore Funções, expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione o Protocolo WebSocket. Selecione Próximo.
- Se não forem necessários recursos adicionais, selecione Avançar.
- Selecione Instalar.
- Quando a instalação for concluída, selecione Fechar para sair do assistente.
Para habilitar o suporte para o protocolo WebSocket no Windows 8 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Navegue até Painel de Controle>Programas>Programas e Recursos>Ativar ou desativar recursos do Windows (lado esquerdo da tela).
- Abra os nós a seguir: Serviços de Informações da Internet>Serviços da World Wide Web>Recursos de Desenvolvimento de Aplicativos.
- Selecione o recurso Protocolo WebSocket. Selecione OK.
Desabilitar WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte do WebSocket em socket.io no Node.js, desabilite o módulo WebSocket do IIS padrão usando o elemento webSocket em web.config ou applicationHost.config. Se essa etapa não for executada, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez de Node.js e o aplicativo.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Aplicativo de exemplo
O aplicativo de exemplo que acompanha este artigo é um aplicativo de eco. Ele tem uma página da Web que faz conexões do WebSocket e o servidor reenvia para o cliente todas as mensagens recebidas. O aplicativo de amostra dá suporte a WebSockets via HTTP/2 ao usar uma estrutura de destino do .NET 7 ou posterior.
Executar o aplicativo:
- Para executar o aplicativo no Visual Studio: abra o projeto de amostra no Visual Studio e pressione Ctrl+F5 para ser executado sem o depurador.
- Para executar o aplicativo em um shell de comando: execute o comando
dotnet rune navegue em um navegador atéhttp://localhost:<port>.
A página da Web mostra o status de conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL exibida. Insira uma mensagem de teste e selecione Enviar. Quando terminar, selecione Fechar Soquete. A seção Log de Comunicação relata cada ação de abertura, envio e fechamento conforme ela ocorre.
Este artigo explica como começar a usar o WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais em conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como chat, painel e aplicativos de jogos.
Exibir ou fazer download do código de exemplo (como fazer download, como executar).
SignalR
ASP.NET Core SignalR é uma biblioteca que simplifica a adição da funcionalidade da Web em tempo real aos aplicativos. Ele usa WebSockets sempre que possível.
Para a maioria dos aplicativos, recomendamos o SignalR em vez de WebSockets puros. SignalR fornece uma alternativa de transporte para ambientes em que WebSockets não estão disponíveis. Também fornece um modelo básico de aplicativo de chamada de procedimento remoto. E, na maioria dos cenários, SignalR não tem nenhuma desvantagem de desempenho significativa em comparação ao uso de WebSockets brutos.
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa para WebSockets.
Prerequisites
- Qualquer sistema operacional compatível com o ASP.NET Core:
- Windows 7/Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo é executado no Windows com o IIS:
- Windows 8/Windows Server 2012 ou posterior
- IIS 8/IIS 8 Express
- WebSockets devem estar habilitados. Consulte a seção Suporte do IIS/IIS Express.
- Se o aplicativo é executado no HTTP.sys:
- Windows 8/Windows Server 2012 ou posterior
- Para saber quais são os navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware de WebSockets em Program.cs:
app.UseWebSockets();
As seguintes configurações podem ser definidas:
- KeepAliveInterval - Com que frequência enviar quadros de "ping" ao cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins – Uma lista de valores de cabeçalho de origem permitidos para solicitações do WebSocket. Por padrão, todas as origens são permitidas. Para obter mais informações, consulte Restrição de origem do WebSocket neste artigo.
var webSocketOptions = new WebSocketOptions
{
KeepAliveInterval = TimeSpan.FromMinutes(2)
};
app.UseWebSockets(webSocketOptions);
Aceitar solicitações do WebSocket
Futuramente no ciclo de vida da solicitação (mais tarde em Program.cs ou em um método de ação, por exemplo), verifique se é uma solicitação do WebSocket e aceite-a.
O exemplo a seguir é de mais adiante em 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);
}
});
Uma solicitação do WebSocket pode entrar em qualquer URL, mas esse código de exemplo aceita apenas solicitações de /ws.
Uma abordagem semelhante pode ser adotada em um método de controlador:
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;
}
}
Ao usar um WebSocket, você deve manter o pipeline de middleware em execução durante a conexão. Se tentar enviar ou receber uma mensagem do WebSocket após o término do pipeline de middleware, você poderá receber uma exceção semelhante à seguinte:
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 você estiver usando um serviço em segundo plano para gravar dados em um WebSocket, mantenha o pipeline do middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e faça-o chamar TrySetResult quando terminar com o WebSocket. Em seguida await a propriedade Task durante a solicitação, conforme é mostrado no exemplo a seguir:
app.Run(async (context) =>
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
});
A exceção fechada do WebSocket também poderá ocorrer ao retornar muito rapidamente de um método de ação. Ao aceitar um soquete de um método de ação, aguarde até que o código que usa o soquete seja concluído antes de retornar do método de ação.
Nunca use Task.Wait, Task.Result ou chamadas de bloqueio semelhantes para aguardar a conclusão do soquete, pois isso pode causar sérios problemas de threading. Sempre use await.
Compression
Warning
Habilitar a compactação em conexões criptografadas pode tornar um aplicativo sujeito a ataques CRIME/BREACH.
Se estiver enviando informações confidenciais, evite habilitar a compactação ou use WebSocketMessageFlags.DisableCompression ao chamar WebSocket.SendAsync.
Isso se aplica a ambos os lados do WebSocket. Observe que a API dos WebSockets no navegador não tem configuração para desabilitar a compactação por envio.
Caso deseje fazer a compactação de mensagens por WebSockets, o código de aceitação deverá especificar que ele permite a compactação da seguinte maneira:
using (var webSocket = await context.WebSockets.AcceptWebSocketAsync(
new WebSocketAcceptContext { DangerousEnableCompression = true }))
{
}
WebSocketAcceptContext.ServerMaxWindowBits e WebSocketAcceptContext.DisableServerContextTakeover são opções avançadas que controlam como a compactação funciona.
A compactação é negociada entre o cliente e o servidor ao estabelecer uma conexão pela primeira vez. Você pode ler mais sobre a negociação na RFC sobre Extensões de Compactação para WebSocket.
Note
Se a negociação de compactação não for aceita pelo servidor ou pelo cliente, a conexão ainda será estabelecida. No entanto, a conexão não usa compactação ao enviar e receber mensagens.
Enviar e receber mensagens
O método AcceptWebSocketAsync atualiza a conexão TCP para uma conexão WebSocket e fornece um objeto WebSocket. Use o objeto WebSocket para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação do WebSocket passa o objeto WebSocket para um método Echo. O código recebe uma mensagem e envia de volta imediatamente a mesma mensagem. As mensagens são enviadas e recebidas em um loop até que o cliente feche a conexão:
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);
}
Ao aceitar a conexão WebSocket antes de iniciar o loop, o pipeline de middleware é encerrado. Ao fechar o soquete, o pipeline é desenrolado. Ou seja, a solicitação deixa de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação continua a avançar no pipeline.
Lidar com desconexões de clientes
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas se o cliente a enviar, o que não pode ser feito se a conexão com a internet for perdida. Se quiser tomar alguma medida quando isso ocorrer, defina um tempo limite, caso não receba nada do cliente após uma determinada janela de tempo.
Se o cliente nem sempre estiver enviando mensagens e você não quiser que ocorra um tempo limite apenas porque a conexão ficou ociosa, faça o cliente usar um temporizador para enviar uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não chegar dentro de 2*X segundos após a mensagem anterior, encerre a conexão e informe que o cliente se desconectou. Aguarde o dobro do intervalo de tempo esperado a fim de deixar um tempo extra para atrasos na rede, que possam atrasar a mensagem de ping.
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam ao WebSockets. Navegadores não:
- Executar solicitações de preflight do CORS.
- Respeite as restrições especificadas nos cabeçalhos
Access-Controlao fazer solicitações WebSocket.
No entanto, os navegadores enviam o cabeçalho Origin ao emitir solicitações de WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos e garantir que apenas WebSockets provenientes de origens esperadas sejam permitidos.
Se você estiver hospedando o servidor em "https://server.com" e hospedando seu cliente em "https://client.com", adicione "https://client.com" à lista AllowedOrigins para que seja feita a verificação pelos 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);
Note
O cabeçalho Origin é controlado pelo cliente e, como o cabeçalho Referer, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte ao IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com o IIS/IIS Express 8 ou posterior são compatíveis com o protocolo WebSocket.
Note
WebSockets estão sempre habilitados ao usar o IIS Express.
Habilitar WebSockets no IIS
Para habilitar o suporte para o protocolo WebSocket no Windows Server 2012 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Use o assistente Adicionar Funções e Recursos por meio do menu Gerenciar ou do link no Gerenciador do Servidor.
- Selecione Instalação baseada em função ou em recurso. Selecione Próximo.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Próximo.
- Expanda Servidor Web (IIS) na árvore Funções, expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione o Protocolo WebSocket. Selecione Próximo.
- Se não forem necessários recursos adicionais, selecione Avançar.
- Selecione Instalar.
- Quando a instalação for concluída, selecione Fechar para sair do assistente.
Para habilitar o suporte para o protocolo WebSocket no Windows 8 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Navegue até Painel de Controle>Programas>Programas e Recursos>Ativar ou desativar recursos do Windows (lado esquerdo da tela).
- Abra os nós a seguir: Serviços de Informações da Internet>Serviços da World Wide Web>Recursos de Desenvolvimento de Aplicativos.
- Selecione o recurso Protocolo WebSocket. Selecione OK.
Desabilitar WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte do WebSocket em socket.io no Node.js, desabilite o módulo WebSocket do IIS padrão usando o elemento webSocket em web.config ou applicationHost.config. Se essa etapa não for executada, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez de Node.js e o aplicativo.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Aplicativo de exemplo
O aplicativo de exemplo que acompanha este artigo é um aplicativo de eco. Ele tem uma página da Web que faz conexões do WebSocket e o servidor reenvia para o cliente todas as mensagens recebidas. O aplicativo de amostra não está configurado para ser executado no Visual Studio com IIS Express, portanto, execute o aplicativo em um shell de comando com dotnet run e navegue em um navegador até http://localhost:<port>. A página da Web mostra o status de conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL exibida. Insira uma mensagem de teste e selecione Enviar. Quando terminar, selecione Fechar Soquete. A seção Log de Comunicação relata cada ação de abertura, envio e fechamento conforme ela ocorre.
Este artigo explica como começar a usar o WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais em conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como chat, painel e aplicativos de jogos.
Exibir ou baixar um código de exemplo (como baixar). Como executar.
SignalR
ASP.NET Core SignalR é uma biblioteca que simplifica a adição da funcionalidade da Web em tempo real aos aplicativos. Ele usa WebSockets sempre que possível.
Para a maioria dos aplicativos, recomendamos o SignalR em vez de WebSockets puros. SignalR fornece uma alternativa de transporte para ambientes em que WebSockets não estão disponíveis. Também fornece um modelo básico de aplicativo de chamada de procedimento remoto. E, na maioria dos cenários, SignalR não tem nenhuma desvantagem de desempenho significativa em comparação ao uso de WebSockets brutos.
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa para WebSockets.
Prerequisites
- Qualquer sistema operacional compatível com o ASP.NET Core:
- Windows 7/Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo é executado no Windows com o IIS:
- Windows 8/Windows Server 2012 ou posterior
- IIS 8/IIS 8 Express
- WebSockets devem estar habilitados. Consulte a seção Suporte do IIS/IIS Express.
- Se o aplicativo é executado no HTTP.sys:
- Windows 8/Windows Server 2012 ou posterior
- Para saber quais são os navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware do WebSockets no método Configure da classe Startup:
app.UseWebSockets();
Note
Se você quiser aceitar solicitações de WebSocket em um controlador, a chamada para app.UseWebSockets deverá ocorrer antes de app.UseEndpoints.
As seguintes configurações podem ser definidas:
- KeepAliveInterval - Com que frequência enviar quadros de "ping" ao cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins – Uma lista de valores de cabeçalho de origem permitidos para solicitações do WebSocket. Por padrão, todas as origens são permitidas. Consulte "Restrição de origem do WebSocket" abaixo para obter detalhes.
var webSocketOptions = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
};
app.UseWebSockets(webSocketOptions);
Aceitar solicitações do WebSocket
Futuramente no ciclo de vida da solicitação (mais tarde no método Configure ou em um método de ação, por exemplo), verifique se é uma solicitação do WebSocket e aceite-a.
O exemplo a seguir é de uma fase posterior do método 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();
}
});
Uma solicitação do WebSocket pode entrar em qualquer URL, mas esse código de exemplo aceita apenas solicitações de /ws.
Uma abordagem semelhante pode ser adotada em um método de controlador:
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;
}
}
Ao usar um WebSocket, você deve manter o pipeline de middleware em execução durante a conexão. Se tentar enviar ou receber uma mensagem do WebSocket após o término do pipeline de middleware, você poderá receber uma exceção semelhante à seguinte:
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 você estiver usando um serviço em segundo plano para gravar dados em um WebSocket, mantenha o pipeline do middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e faça-o chamar TrySetResult quando terminar com o WebSocket. Em seguida await a propriedade Task durante a solicitação, conforme é mostrado no exemplo a seguir:
app.Use(async (context, next) =>
{
using (WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync())
{
var socketFinishedTcs = new TaskCompletionSource<object>();
BackgroundSocketProcessor.AddSocket(webSocket, socketFinishedTcs);
await socketFinishedTcs.Task;
}
});
A exceção fechada do WebSocket também poderá ocorrer ao retornar muito rapidamente de um método de ação. Ao aceitar um soquete de um método de ação, aguarde até que o código que usa o soquete seja concluído antes de retornar do método de ação.
Nunca use Task.Wait, Task.Result ou chamadas de bloqueio semelhantes para aguardar a conclusão do soquete, pois isso pode causar sérios problemas de threading. Sempre use await.
Enviar e receber mensagens
O método AcceptWebSocketAsync atualiza a conexão TCP para uma conexão WebSocket e fornece um objeto WebSocket. Use o objeto WebSocket para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação do WebSocket passa o objeto WebSocket para um método Echo. O código recebe uma mensagem e envia de volta imediatamente a mesma mensagem. As mensagens são enviadas e recebidas em um loop até que o cliente feche a conexão:
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);
}
Ao aceitar a conexão WebSocket antes de iniciar o loop, o pipeline de middleware é encerrado. Ao fechar o soquete, o pipeline é desenrolado. Ou seja, a solicitação deixa de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação continua a avançar no pipeline.
Lidar com desconexões de clientes
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas se o cliente a enviar, o que não pode ser feito se a conexão com a internet for perdida. Se quiser tomar alguma medida quando isso ocorrer, defina um tempo limite, caso não receba nada do cliente após uma determinada janela de tempo.
Se o cliente nem sempre estiver enviando mensagens e você não quiser que ocorra um tempo limite apenas porque a conexão ficou ociosa, faça o cliente usar um temporizador para enviar uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não chegar dentro de 2*X segundos após a mensagem anterior, encerre a conexão e informe que o cliente se desconectou. Aguarde o dobro do intervalo de tempo esperado a fim de deixar um tempo extra para atrasos na rede, que possam atrasar a mensagem de ping.
Note
O ManagedWebSocket interno manipula os quadros de Ping/Pong implicitamente para manter a conexão ativa se a opção KeepAliveInterval for maior que zero, cujo padrão é 30 segundos (TimeSpan.FromSeconds(30)).
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam ao WebSockets. Navegadores não:
- Executar solicitações de preflight do CORS.
- Respeite as restrições especificadas nos cabeçalhos
Access-Controlao fazer solicitações WebSocket.
No entanto, os navegadores enviam o cabeçalho Origin ao emitir solicitações de WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos e garantir que apenas WebSockets provenientes de origens esperadas sejam permitidos.
Se você estiver hospedando o servidor em "https://server.com" e hospedando seu cliente em "https://client.com", adicione "https://client.com" à lista AllowedOrigins para que seja feita a verificação pelos 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);
Note
O cabeçalho Origin é controlado pelo cliente e, como o cabeçalho Referer, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte ao IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com o IIS/IIS Express 8 ou posterior são compatíveis com o protocolo WebSocket.
Note
WebSockets estão sempre habilitados ao usar o IIS Express.
Habilitar WebSockets no IIS
Para habilitar o suporte para o protocolo WebSocket no Windows Server 2012 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Use o assistente Adicionar Funções e Recursos por meio do menu Gerenciar ou do link no Gerenciador do Servidor.
- Selecione Instalação baseada em função ou em recurso. Selecione Próximo.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Próximo.
- Expanda Servidor Web (IIS) na árvore Funções, expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione o Protocolo WebSocket. Selecione Próximo.
- Se não forem necessários recursos adicionais, selecione Avançar.
- Selecione Instalar.
- Quando a instalação for concluída, selecione Fechar para sair do assistente.
Para habilitar o suporte para o protocolo WebSocket no Windows 8 ou posterior:
Note
Estas etapas não são necessárias ao usar o IIS Express
- Navegue até Painel de Controle>Programas>Programas e Recursos>Ativar ou desativar recursos do Windows (lado esquerdo da tela).
- Abra os nós a seguir: Serviços de Informações da Internet>Serviços da World Wide Web>Recursos de Desenvolvimento de Aplicativos.
- Selecione o recurso Protocolo WebSocket. Selecione OK.
Desabilitar WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte do WebSocket em socket.io no Node.js, desabilite o módulo WebSocket do IIS padrão usando o elemento webSocket em web.config ou applicationHost.config. Se essa etapa não for executada, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez de Node.js e o aplicativo.
<system.webServer>
<webSocket enabled="false" />
</system.webServer>
Aplicativo de exemplo
O aplicativo de exemplo que acompanha este artigo é um aplicativo de eco. Ele tem uma página da Web que faz conexões do WebSocket e o servidor reenvia para o cliente todas as mensagens recebidas. O aplicativo de amostra não está configurado para ser executado no Visual Studio com IIS Express, portanto, execute o aplicativo em um shell de comando com dotnet run e navegue em um navegador até http://localhost:5000. A página da Web mostra o status de conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL exibida. Insira uma mensagem de teste e selecione Enviar. Quando terminar, selecione Fechar Soquete. A seção Log de Comunicação relata cada ação de abertura, envio e fechamento conforme ela ocorre.