Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. 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 .NET 10 deste artigo.
Warning
Esta versão do ASP.NET Core não é mais suportada. 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 .NET 9 deste artigo.
Este artigo explica como começar a usar WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais através de conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como aplicativos de bate-papo, painel e jogos.
Visualize ou baixe o código de exemplo (como baixar, como executar).
Suporte a Http/2 WebSockets
O uso de WebSockets sobre HTTP/2 aproveita os 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 suportados estão disponíveis em Kestrel em todas as plataformas habilitadas para HTTP/2. A negociação de versão é automática em navegadores e Kestrel, portanto, não são necessárias novas APIs.
O .NET 7 introduziu suporte para WebSockets via 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 ativados 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 sobre HTTP/2. (RFC 8441)
SignalR
ASP.NET Núcleo SignalR é uma biblioteca que simplifica a adição de funcionalidade da Web em tempo real aos aplicativos. Utiliza WebSockets sempre que possível.
Para a maioria das aplicações, recomendamos SignalR em vez de WebSockets brutos. SignalR:
- Fornece alternativa de transporte para ambientes onde 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 com o uso de WebSockets brutos na maioria dos cenários.
O suporte a WebSockets sobre HTTP/2 está disponível para:
- ASP.NET Core cliente JavaScript SignalR
- ASP.NET Core SignalR com Blazor WebAssembly
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa aos WebSockets.
Prerequisites
- Qualquer SO que suporte ASP.NET Core:
- Windows 7 / Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo for executado no Windows com o IIS:
- Windows 8 / Windows Server 2012 ou posterior
- IIS 8 Express / IIS 8
- WebSockets devem estar habilitados. Consulte a seção de suporte do IIS/IIS Express .
- Se a aplicação for executada em HTTP.sys:
- Windows 8 / Windows Server 2012 ou posterior
- Para navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware WebSockets em Program.cs:
app.UseWebSockets();
As seguintes configurações podem ser configuradas:
- KeepAliveInterval - Com que frequência enviar quadros "ping" para o cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins - Uma lista de valores de cabeçalho Origin permitidos para solicitações 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 WebSocket
Em algum lugar mais tarde no ciclo de vida da solicitação (mais tarde em Program.cs ou num método de ação, por exemplo), verifique se é uma solicitação WebSocket e aceite esta solicitação.
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 WebSocket pode entrar em qualquer URL, mas esse código de exemplo só aceita solicitações para /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 você tentar enviar ou receber uma mensagem WebSocket após o pipeline de middleware terminar, você pode obter uma exceção como a 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, certifique-se de manter o pipeline de middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e chame TrySetResult quando terminar com o WebSocket. Em seguida, altere await a Task propriedade 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 pode acontecer ao retornar muito cedo de um método de ação. Ao aceitar um soquete em um método de ação, aguarde a conclusão do código que usa o soquete 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 socket, pois isso pode causar sérios problemas de threading. Utilize sempre await.
Adicionar suporte a WebSockets HTTP/2 para controladores existentes
O .NET 7 introduziu suporte para WebSockets via 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 usá-lo [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 compressão em conexões criptografadas pode tornar uma aplicação sujeita a CRIME/BREACH ataques.
Se enviar informações confidenciais, evite ativar a compactação ou usar WebSocketMessageFlags.DisableCompression ao ligar WebSocket.SendAsync.
Isso se aplica a ambos os lados do WebSocket. Observe que a API WebSockets no navegador não tem configuração para desabilitar a compactação por envio.
Se a compactação de mensagens sobre WebSockets for desejada, o código de aceitação deve 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 funciona a compressão.
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 em Extensões de compactação para WebSocket RFC.
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 AcceptWebSocketAsync método atualiza a conexão TCP para uma conexão WebSocket e fornece um WebSocket objeto. Use o WebSocket objeto para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação WebSocket passa o WebSocket objeto para um Echo método. O código recebe uma mensagem e imediatamente envia de volta 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 uma conexão WebSocket antes de iniciar o loop, o pipeline do middleware é encerrado. Ao fechar a tomada, a tubulação se desenrola. Ou seja, a solicitação para de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação retorna ao 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 somente se o cliente enviá-la, o que não pode ser feito se a conexão com a Internet for perdida. Se você quiser tomar alguma ação quando isso acontecer, defina um tempo limite depois que nada for recebido do cliente dentro de uma determinada janela de tempo.
Se o cliente não estiver sempre a enviar mensagens e não quiser que a conexão fique sem resposta apenas porque fica ociosa, instrua o cliente a usar um temporizador que envia uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não tiver chegado dentro de 2 * X segundos após a anterior, encerre a conexão e informe que o cliente desconectou. Aguarde o dobro do intervalo de tempo esperado para deixar tempo extra para atrasos de rede que possam atrasar a mensagem ping.
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam a WebSockets. Os navegadores não:
- Execute solicitações de pré-voo do CORS.
- Respeite as restrições especificadas nos
Access-Controlcabeçalhos ao fazer solicitações WebSocket.
No entanto, os navegadores enviam o Origin cabeçalho ao emitir solicitações WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos para garantir que apenas WebSockets provenientes das origens esperadas sejam permitidos.
Se estiveres a hospedar o teu servidor em "https://server.com"" e o teu cliente em "https://client.com"", deves adicionar "https://client.com"" à lista de AllowedOrigins para verificar os 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 Origin cabeçalho é controlado pelo cliente e, como o Referer cabeçalho, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte a IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com IIS/IIS Express 8 ou posterior têm suporte para o protocolo WebSocket, mas não para WebSockets sobre HTTP/2.
Note
WebSockets são sempre habilitados ao usar o IIS Express.
Habilitando 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 no menu Gerenciar ou no link Gerenciador do Servidor.
- Selecione Instalação baseada em função ou recurso. Selecione Avançar.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Avançar.
- Expanda Servidor Web (IIS) na árvore Funções , expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione Protocolo WebSocket. Selecione Avançar.
- Se não forem necessárias funcionalidades adicionais, selecione Seguinte.
- 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 seguintes nós: Serviços de Informação da Internet>Serviços da World Wide Web>Funcionalidades de Desenvolvimento de Aplicações.
- Selecione o recurso WebSocket Protocol. Selecione OK.
Desative o WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte a WebSocket no socket.io no Node.js, desative o módulo WebSocket do IIS padrão usando o webSocket elemento em web.config ou applicationHost.config. Se esta etapa não for executada, o módulo WebSocket do IIS tentará manipular a comunicação 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 WebSocket, e o servidor reenvia todas as mensagens que recebe de volta para o cliente. O aplicativo de exemplo oferece suporte a WebSockets sobre HTTP/2 ao usar uma estrutura de destino do .NET 7 ou posterior.
Execute o aplicativo:
- Para executar o aplicativo no Visual Studio: Abra o projeto de exemplo no Visual Studio e pressione Ctrl+F5 para executar 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 da conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL mostrada. 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 abrir, enviar e fechar conforme ocorre.
Este artigo explica como começar a usar WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais através de conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como aplicativos de bate-papo, painel e jogos.
Visualize ou baixe o código de exemplo (como baixar, como executar).
Suporte a Http/2 WebSockets
O uso de WebSockets sobre HTTP/2 aproveita os 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 suportados estão disponíveis em Kestrel em todas as plataformas habilitadas para HTTP/2. A negociação de versão é automática em navegadores e Kestrel, portanto, não são necessárias novas APIs.
O .NET 7 introduziu suporte para WebSockets via 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 ativados 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 sobre HTTP/2. (RFC 8441)
SignalR
ASP.NET Núcleo SignalR é uma biblioteca que simplifica a adição de funcionalidade da Web em tempo real aos aplicativos. Utiliza WebSockets sempre que possível.
Para a maioria das aplicações, recomendamos SignalR em vez de WebSockets brutos. SignalR:
- Fornece alternativa de transporte para ambientes onde 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 com o uso de WebSockets brutos na maioria dos cenários.
O suporte a WebSockets sobre HTTP/2 está disponível para:
- ASP.NET Core cliente JavaScript SignalR
- ASP.NET Core SignalR com Blazor WebAssembly
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa aos WebSockets.
Prerequisites
- Qualquer SO que suporte ASP.NET Core:
- Windows 7 / Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo for executado no Windows com o IIS:
- Windows 8 / Windows Server 2012 ou posterior
- IIS 8 / IIS 8 Expresso
- WebSockets devem estar habilitados. Consulte a seção de suporte do IIS/IIS Express .
- Se a aplicação for executada em HTTP.sys:
- Windows 8 / Windows Server 2012 ou posterior
- Para navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware WebSockets em Program.cs:
app.UseWebSockets();
As seguintes configurações podem ser configuradas:
- KeepAliveInterval - Com que frequência enviar quadros "ping" para o cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins - Uma lista de valores de cabeçalho Origin permitidos para solicitações 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 WebSocket
Em algum lugar mais tarde no ciclo de vida da solicitação (mais tarde em Program.cs ou num método de ação, por exemplo), verifique se é uma solicitação WebSocket e aceite esta solicitação.
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 WebSocket pode entrar em qualquer URL, mas esse código de exemplo só aceita solicitações para /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 você tentar enviar ou receber uma mensagem WebSocket após o pipeline de middleware terminar, você pode obter uma exceção como a 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, certifique-se de manter o pipeline de middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e chame TrySetResult quando terminar com o WebSocket. Em seguida, altere await a Task propriedade 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 pode acontecer ao retornar muito cedo de um método de ação. Ao aceitar um soquete em um método de ação, aguarde a conclusão do código que usa o soquete 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 socket, pois isso pode causar sérios problemas de threading. Utilize sempre await.
Adicionar suporte a WebSockets HTTP/2 para controladores existentes
O .NET 7 introduziu suporte para WebSockets via 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 usá-lo [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 compressão em conexões criptografadas pode tornar uma aplicação sujeita a CRIME/BREACH ataques.
Se enviar informações confidenciais, evite ativar a compactação ou usar WebSocketMessageFlags.DisableCompression ao ligar WebSocket.SendAsync.
Isso se aplica a ambos os lados do WebSocket. Observe que a API WebSockets no navegador não tem configuração para desabilitar a compactação por envio.
Se a compactação de mensagens sobre WebSockets for desejada, o código de aceitação deve 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 funciona a compressão.
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 em Extensões de compactação para WebSocket RFC.
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 AcceptWebSocketAsync método atualiza a conexão TCP para uma conexão WebSocket e fornece um WebSocket objeto. Use o WebSocket objeto para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação WebSocket passa o WebSocket objeto para um Echo método. O código recebe uma mensagem e imediatamente envia de volta 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 uma conexão WebSocket antes de iniciar o loop, o pipeline do middleware é encerrado. Ao fechar a tomada, a tubulação se desenrola. Ou seja, a solicitação para de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação retorna ao 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 somente se o cliente enviá-la, o que não pode ser feito se a conexão com a Internet for perdida. Se você quiser tomar alguma ação quando isso acontecer, defina um tempo limite depois que nada for recebido do cliente dentro de uma determinada janela de tempo.
Se o cliente não estiver sempre a enviar mensagens e não quiser que a conexão fique sem resposta apenas porque fica ociosa, instrua o cliente a usar um temporizador que envia uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não tiver chegado dentro de 2 * X segundos após a anterior, encerre a conexão e informe que o cliente desconectou. Aguarde o dobro do intervalo de tempo esperado para deixar tempo extra para atrasos de rede que possam atrasar a mensagem ping.
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam a WebSockets. Os navegadores não:
- Execute solicitações de pré-voo do CORS.
- Respeite as restrições especificadas nos
Access-Controlcabeçalhos ao fazer solicitações WebSocket.
No entanto, os navegadores enviam o Origin cabeçalho ao emitir solicitações WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos para garantir que apenas WebSockets provenientes das origens esperadas sejam permitidos.
Se estiveres a hospedar o teu servidor em "https://server.com"" e o teu cliente em "https://client.com"", deves adicionar "https://client.com"" à lista de AllowedOrigins para verificar os 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 Origin cabeçalho é controlado pelo cliente e, como o Referer cabeçalho, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte a IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com IIS/IIS Express 8 ou posterior têm suporte para o protocolo WebSocket, mas não para WebSockets sobre HTTP/2.
Note
WebSockets são sempre habilitados ao usar o IIS Express.
Habilitando 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 no menu Gerenciar ou no link Gerenciador do Servidor.
- Selecione Instalação baseada em função ou recurso. Selecione Avançar.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Avançar.
- Expanda Servidor Web (IIS) na árvore Funções , expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione Protocolo WebSocket. Selecione Avançar.
- Se não forem necessárias funcionalidades adicionais, selecione Seguinte.
- 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 seguintes nós: Serviços de Informação da Internet>Serviços da World Wide Web>Funcionalidades de Desenvolvimento de Aplicações.
- Selecione o recurso WebSocket Protocol. Selecione OK.
Desative o WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte a WebSocket no socket.io no Node.js, desative o módulo WebSocket do IIS padrão usando o webSocket elemento em web.config ou applicationHost.config. Se esta etapa não for executada, o módulo WebSocket do IIS tentará manipular a comunicação 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 WebSocket, e o servidor reenvia todas as mensagens que recebe de volta para o cliente. O aplicativo de exemplo oferece suporte a WebSockets sobre HTTP/2 ao usar uma estrutura de destino do .NET 7 ou posterior.
Execute o aplicativo:
- Para executar o aplicativo no Visual Studio: Abra o projeto de exemplo no Visual Studio e pressione Ctrl+F5 para executar 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 da conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL mostrada. 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 abrir, enviar e fechar conforme ocorre.
Este artigo explica como começar a usar WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais através de conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como aplicativos de bate-papo, painel e jogos.
Visualize ou baixe o código de exemplo (como baixar, como executar).
Suporte a Http/2 WebSockets
O uso de WebSockets sobre HTTP/2 aproveita os 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 suportados estão disponíveis em Kestrel em todas as plataformas habilitadas para HTTP/2. A negociação de versão é automática em navegadores e Kestrel, portanto, não são necessárias novas APIs.
O .NET 7 introduziu suporte para WebSockets via 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 ativados 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 sobre HTTP/2. (RFC 8441)
SignalR
ASP.NET Núcleo SignalR é uma biblioteca que simplifica a adição de funcionalidade da Web em tempo real aos aplicativos. Utiliza WebSockets sempre que possível.
Para a maioria das aplicações, recomendamos SignalR em vez de WebSockets brutos. SignalR:
- Fornece alternativa de transporte para ambientes onde 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 com o uso de WebSockets brutos na maioria dos cenários.
O suporte a WebSockets sobre HTTP/2 está disponível para:
- ASP.NET Core cliente JavaScript SignalR
- ASP.NET Core SignalR com Blazor WebAssembly
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa aos WebSockets.
Prerequisites
- Qualquer SO que suporte ASP.NET Core:
- Windows 7 / Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo for executado no Windows com o IIS:
- Windows 8 / Windows Server 2012 ou posterior
- Expresso IIS 8 / IIS 8
- WebSockets devem estar habilitados. Consulte a seção de suporte do IIS/IIS Express .
- Se a aplicação for executada em HTTP.sys:
- Windows 8 / Windows Server 2012 ou posterior
- Para navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware WebSockets em Program.cs:
app.UseWebSockets();
As seguintes configurações podem ser configuradas:
- KeepAliveInterval - Com que frequência enviar quadros "ping" para o cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins - Uma lista de valores de cabeçalho Origin permitidos para solicitações 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 WebSocket
Em algum lugar mais tarde no ciclo de vida da solicitação (mais tarde em Program.cs ou num método de ação, por exemplo), verifique se é uma solicitação WebSocket e aceite esta solicitação.
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 WebSocket pode entrar em qualquer URL, mas esse código de exemplo só aceita solicitações para /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 você tentar enviar ou receber uma mensagem WebSocket após o pipeline de middleware terminar, você pode obter uma exceção como a 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, certifique-se de manter o pipeline de middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e chame TrySetResult quando terminar com o WebSocket. Em seguida, altere await a Task propriedade 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 pode acontecer ao retornar muito cedo de um método de ação. Ao aceitar um soquete em um método de ação, aguarde a conclusão do código que usa o soquete 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 socket, pois isso pode causar sérios problemas de threading. Utilize sempre await.
Adicionar suporte a WebSockets HTTP/2 para controladores existentes
O .NET 7 introduziu suporte para WebSockets via 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 usá-lo [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 compressão em conexões criptografadas pode tornar uma aplicação sujeita a CRIME/BREACH ataques.
Se enviar informações confidenciais, evite ativar a compactação ou usar WebSocketMessageFlags.DisableCompression ao ligar WebSocket.SendAsync.
Isso se aplica a ambos os lados do WebSocket. Observe que a API WebSockets no navegador não tem configuração para desabilitar a compactação por envio.
Se a compactação de mensagens sobre WebSockets for desejada, o código de aceitação deve 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 funciona a compressão.
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 em Extensões de compactação para WebSocket RFC.
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 AcceptWebSocketAsync método atualiza a conexão TCP para uma conexão WebSocket e fornece um WebSocket objeto. Use o WebSocket objeto para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação WebSocket passa o WebSocket objeto para um Echo método. O código recebe uma mensagem e imediatamente envia de volta 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 uma conexão WebSocket antes de iniciar o loop, o pipeline do middleware é encerrado. Ao fechar a tomada, a tubulação se desenrola. Ou seja, a solicitação para de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação retorna ao 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 somente se o cliente enviá-la, o que não pode ser feito se a conexão com a Internet for perdida. Se você quiser tomar alguma ação quando isso acontecer, defina um tempo limite depois que nada for recebido do cliente dentro de uma determinada janela de tempo.
Se o cliente não estiver sempre a enviar mensagens e não quiser que a conexão fique sem resposta apenas porque fica ociosa, instrua o cliente a usar um temporizador que envia uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não tiver chegado dentro de 2 * X segundos após a anterior, encerre a conexão e informe que o cliente desconectou. Aguarde o dobro do intervalo de tempo esperado para deixar tempo extra para atrasos de rede que possam atrasar a mensagem ping.
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam a WebSockets. Os navegadores não:
- Execute solicitações de pré-voo do CORS.
- Respeite as restrições especificadas nos
Access-Controlcabeçalhos ao fazer solicitações WebSocket.
No entanto, os navegadores enviam o Origin cabeçalho ao emitir solicitações WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos para garantir que apenas WebSockets provenientes das origens esperadas sejam permitidos.
Se estiveres a hospedar o teu servidor em "https://server.com"" e o teu cliente em "https://client.com"", deves adicionar "https://client.com"" à lista de AllowedOrigins para verificar os 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 Origin cabeçalho é controlado pelo cliente e, como o Referer cabeçalho, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte a IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com IIS/IIS Express 8 ou posterior têm suporte para o protocolo WebSocket, mas não para WebSockets sobre HTTP/2.
Note
WebSockets são sempre habilitados ao usar o IIS Express.
Habilitando 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 no menu Gerenciar ou no link Gerenciador do Servidor.
- Selecione Instalação baseada em função ou recurso. Selecione Avançar.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Avançar.
- Expanda Servidor Web (IIS) na árvore Funções , expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione Protocolo WebSocket. Selecione Avançar.
- Se não forem necessárias funcionalidades adicionais, selecione Seguinte.
- 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 seguintes nós: Serviços de Informação da Internet>Serviços da World Wide Web>Funcionalidades de Desenvolvimento de Aplicações.
- Selecione o recurso WebSocket Protocol. Selecione OK.
Desative o WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte a WebSocket no socket.io no Node.js, desative o módulo WebSocket do IIS padrão usando o webSocket elemento em web.config ou applicationHost.config. Se esta etapa não for executada, o módulo WebSocket do IIS tentará manipular a comunicação 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 WebSocket, e o servidor reenvia todas as mensagens que recebe de volta para o cliente. O aplicativo de exemplo oferece suporte a WebSockets sobre HTTP/2 ao usar uma estrutura de destino do .NET 7 ou posterior.
Execute o aplicativo:
- Para executar o aplicativo no Visual Studio: Abra o projeto de exemplo no Visual Studio e pressione Ctrl+F5 para executar 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 da conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL mostrada. 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 abrir, enviar e fechar conforme ocorre.
Este artigo explica como começar a usar WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais através de conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como aplicativos de bate-papo, painel e jogos.
Visualize ou baixe o código de exemplo (como baixar, como executar).
SignalR
ASP.NET Núcleo SignalR é uma biblioteca que simplifica a adição de funcionalidade da Web em tempo real aos aplicativos. Utiliza WebSockets sempre que possível.
Para a maioria das aplicações, recomendamos SignalR em vez de WebSockets puros. SignalR fornece uma solução alternativa de transporte para ambientes onde o WebSockets não está disponível. Também oferece um modelo básico de aplicação para chamadas de procedimento remoto. E na maioria dos cenários, SignalR não tem nenhuma desvantagem de desempenho significativa em comparação com o uso de WebSockets brutos.
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa aos WebSockets.
Prerequisites
- Qualquer SO que suporte ASP.NET Core:
- Windows 7 / Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo for 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 de suporte do IIS/IIS Express .
- Se a aplicação for executada em HTTP.sys:
- Windows 8 / Windows Server 2012 ou posterior
- Para navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware WebSockets em Program.cs:
app.UseWebSockets();
As seguintes configurações podem ser configuradas:
- KeepAliveInterval - Com que frequência enviar quadros "ping" para o cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins - Uma lista de valores de cabeçalho Origin permitidos para solicitações 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 WebSocket
Em algum lugar mais tarde no ciclo de vida da solicitação (mais tarde em Program.cs ou num método de ação, por exemplo), verifique se é uma solicitação WebSocket e aceite esta solicitação.
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 WebSocket pode entrar em qualquer URL, mas esse código de exemplo só aceita solicitações para /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 você tentar enviar ou receber uma mensagem WebSocket após o pipeline de middleware terminar, você pode obter uma exceção como a 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, certifique-se de manter o pipeline de middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e chame TrySetResult quando terminar com o WebSocket. Em seguida, altere await a Task propriedade 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 pode acontecer ao retornar muito cedo de um método de ação. Ao aceitar um soquete em um método de ação, aguarde a conclusão do código que usa o soquete 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 socket, pois isso pode causar sérios problemas de threading. Utilize sempre await.
Compression
Warning
Habilitar a compressão em conexões criptografadas pode tornar uma aplicação sujeita a CRIME/BREACH ataques.
Se enviar informações confidenciais, evite ativar a compactação ou usar WebSocketMessageFlags.DisableCompression ao ligar WebSocket.SendAsync.
Isso se aplica a ambos os lados do WebSocket. Observe que a API WebSockets no navegador não tem configuração para desabilitar a compactação por envio.
Se a compactação de mensagens sobre WebSockets for desejada, o código de aceitação deve 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 funciona a compressão.
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 em Extensões de compactação para WebSocket RFC.
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 AcceptWebSocketAsync método atualiza a conexão TCP para uma conexão WebSocket e fornece um WebSocket objeto. Use o WebSocket objeto para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação WebSocket passa o WebSocket objeto para um Echo método. O código recebe uma mensagem e imediatamente envia de volta 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 uma conexão WebSocket antes de iniciar o loop, o pipeline do middleware é encerrado. Ao fechar a tomada, a tubulação se desenrola. Ou seja, a solicitação para de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação retorna ao 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 somente se o cliente enviá-la, o que não pode ser feito se a conexão com a Internet for perdida. Se você quiser tomar alguma ação quando isso acontecer, defina um tempo limite depois que nada for recebido do cliente dentro de uma determinada janela de tempo.
Se o cliente não estiver sempre a enviar mensagens e não quiser que a conexão fique sem resposta apenas porque fica ociosa, instrua o cliente a usar um temporizador que envia uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não tiver chegado dentro de 2 * X segundos após a anterior, encerre a conexão e informe que o cliente desconectou. Aguarde o dobro do intervalo de tempo esperado para deixar tempo extra para atrasos de rede que possam atrasar a mensagem ping.
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam a WebSockets. Os navegadores não:
- Execute solicitações de pré-voo do CORS.
- Respeite as restrições especificadas nos
Access-Controlcabeçalhos ao fazer solicitações WebSocket.
No entanto, os navegadores enviam o Origin cabeçalho ao emitir solicitações WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos para garantir que apenas WebSockets provenientes das origens esperadas sejam permitidos.
Se estiveres a hospedar o teu servidor em "https://server.com"" e o teu cliente em "https://client.com"", deves adicionar "https://client.com"" à lista de AllowedOrigins para verificar os 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 Origin cabeçalho é controlado pelo cliente e, como o Referer cabeçalho, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte a IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com IIS/IIS Express 8 ou posterior têm suporte para o protocolo WebSocket.
Note
WebSockets são sempre habilitados ao usar o IIS Express.
Habilitando 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 no menu Gerenciar ou no link Gerenciador do Servidor.
- Selecione Instalação baseada em função ou recurso. Selecione Avançar.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Avançar.
- Expanda Servidor Web (IIS) na árvore Funções , expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione Protocolo WebSocket. Selecione Avançar.
- Se não forem necessárias funcionalidades adicionais, selecione Seguinte.
- 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 seguintes nós: Serviços de Informação da Internet>Serviços da World Wide Web>Funcionalidades de Desenvolvimento de Aplicações.
- Selecione o recurso WebSocket Protocol. Selecione OK.
Desative o WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte a WebSocket no socket.io no Node.js, desative o módulo WebSocket do IIS padrão usando o webSocket elemento em web.config ou applicationHost.config. Se esta etapa não for executada, o módulo WebSocket do IIS tentará manipular a comunicação 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 WebSocket, e o servidor reenvia todas as mensagens que recebe de volta para o cliente. O aplicativo de exemplo não está configurado para ser executado a partir do Visual Studio com o IIS Express, portanto, execute o aplicativo em um shell de comando com dotnet run e navegue em um navegador para http://localhost:<port>. A página da Web mostra o status da conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL mostrada. 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 abrir, enviar e fechar conforme ocorre.
Este artigo explica como começar a usar WebSockets no ASP.NET Core. WebSocket (RFC 6455) é um protocolo que permite canais de comunicação persistentes bidirecionais através de conexões TCP. Ele é usado em aplicativos que se beneficiam de comunicação rápida e em tempo real, como aplicativos de bate-papo, painel e jogos.
Visualize ou baixe o código de exemplo (como fazer o download). Como correr.
SignalR
ASP.NET Núcleo SignalR é uma biblioteca que simplifica a adição de funcionalidade da Web em tempo real aos aplicativos. Utiliza WebSockets sempre que possível.
Para a maioria das aplicações, recomendamos SignalR em vez de WebSockets puros. SignalR fornece uma solução alternativa de transporte para ambientes onde o WebSockets não está disponível. Também oferece um modelo básico de aplicação para chamadas de procedimento remoto. E na maioria dos cenários, SignalR não tem nenhuma desvantagem de desempenho significativa em comparação com o uso de WebSockets brutos.
Para alguns aplicativos, o gRPC no .NET fornece uma alternativa aos WebSockets.
Prerequisites
- Qualquer SO que suporte ASP.NET Core:
- Windows 7 / Windows Server 2008 ou posterior
- Linux
- macOS
- Se o aplicativo for 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 de suporte do IIS/IIS Express .
- Se a aplicação for executada em HTTP.sys:
- Windows 8 / Windows Server 2012 ou posterior
- Para navegadores compatíveis, consulte Posso usar.
Configurar o middleware
Adicione o middleware WebSockets no método Configure da classe Startup.
app.UseWebSockets();
Note
Se você quiser aceitar solicitações WebSocket em um controlador, a chamada para app.UseWebSockets deve ocorrer antes de app.UseEndpoints.
As seguintes configurações podem ser configuradas:
- KeepAliveInterval - Com que frequência enviar quadros "ping" para o cliente para garantir que os proxies mantenham a conexão aberta. O padrão é dois minutos.
- AllowedOrigins - Uma lista de valores de cabeçalho Origin permitidos para solicitações 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 WebSocket
Em algum lugar mais tarde no ciclo de vida da solicitação (mais tarde no Configure método ou em um método de ação, por exemplo) verifique se é uma solicitação WebSocket e aceite a solicitação WebSocket.
O exemplo a seguir é mais à frente no Configure método:
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 WebSocket pode entrar em qualquer URL, mas esse código de exemplo só aceita solicitações para /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 você tentar enviar ou receber uma mensagem WebSocket após o pipeline de middleware terminar, você pode obter uma exceção como a 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, certifique-se de manter o pipeline de middleware em execução. Faça isso usando um TaskCompletionSource<TResult>. Passe o TaskCompletionSource ao seu serviço em segundo plano e chame TrySetResult quando terminar com o WebSocket. Em seguida, altere await a Task propriedade 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 pode acontecer ao retornar muito cedo de um método de ação. Ao aceitar um soquete em um método de ação, aguarde a conclusão do código que usa o soquete 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 socket, pois isso pode causar sérios problemas de threading. Utilize sempre await.
Enviar e receber mensagens
O AcceptWebSocketAsync método atualiza a conexão TCP para uma conexão WebSocket e fornece um WebSocket objeto. Use o WebSocket objeto para enviar e receber mensagens.
O código mostrado anteriormente que aceita a solicitação WebSocket passa o WebSocket objeto para um Echo método. O código recebe uma mensagem e imediatamente envia de volta 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 uma conexão WebSocket antes de iniciar o loop, o pipeline do middleware é encerrado. Ao fechar a tomada, a tubulação se desenrola. Ou seja, a solicitação para de avançar no pipeline quando o WebSocket é aceito. Quando o loop é concluído e o soquete é fechado, a solicitação retorna ao 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 somente se o cliente enviá-la, o que não pode ser feito se a conexão com a Internet for perdida. Se você quiser tomar alguma ação quando isso acontecer, defina um tempo limite depois que nada for recebido do cliente dentro de uma determinada janela de tempo.
Se o cliente não estiver sempre a enviar mensagens e não quiser que a conexão fique sem resposta apenas porque fica ociosa, instrua o cliente a usar um temporizador que envia uma mensagem de ping a cada X segundos. No servidor, se uma mensagem não tiver chegado dentro de 2 * X segundos após a anterior, encerre a conexão e informe que o cliente desconectou. Aguarde o dobro do intervalo de tempo esperado para deixar tempo extra para atrasos de rede que possam atrasar a mensagem ping.
Note
O interno ManagedWebSocket manipula os quadros de Ping/Pong implicitamente para manter a conexão ativa se a KeepAliveInterval opção for maior que zero, o padrão é de 30 segundos (TimeSpan.FromSeconds(30)).
Restrição de origem do WebSocket
As proteções fornecidas pelo CORS não se aplicam a WebSockets. Os navegadores não:
- Execute solicitações de pré-voo do CORS.
- Respeite as restrições especificadas nos
Access-Controlcabeçalhos ao fazer solicitações WebSocket.
No entanto, os navegadores enviam o Origin cabeçalho ao emitir solicitações WebSocket. Os aplicativos devem ser configurados para validar esses cabeçalhos para garantir que apenas WebSockets provenientes das origens esperadas sejam permitidos.
Se estiveres a hospedar o teu servidor em "https://server.com"" e o teu cliente em "https://client.com"", deves adicionar "https://client.com"" à lista de AllowedOrigins para verificar os 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 Origin cabeçalho é controlado pelo cliente e, como o Referer cabeçalho, pode ser falsificado.
Não use esses cabeçalhos como um mecanismo de autenticação.
Suporte a IIS/IIS Express
O Windows Server 2012 ou posterior e o Windows 8 ou posterior com IIS/IIS Express 8 ou posterior têm suporte para o protocolo WebSocket.
Note
WebSockets são sempre habilitados ao usar o IIS Express.
Habilitando 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 no menu Gerenciar ou no link Gerenciador do Servidor.
- Selecione Instalação baseada em função ou recurso. Selecione Avançar.
- Selecione o servidor apropriado (o servidor local é selecionado por padrão). Selecione Avançar.
- Expanda Servidor Web (IIS) na árvore Funções , expanda Servidor Web e, em seguida, expanda Desenvolvimento de Aplicativos.
- Selecione Protocolo WebSocket. Selecione Avançar.
- Se não forem necessárias funcionalidades adicionais, selecione Seguinte.
- 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 seguintes nós: Serviços de Informação da Internet>Serviços da World Wide Web>Funcionalidades de Desenvolvimento de Aplicações.
- Selecione o recurso WebSocket Protocol. Selecione OK.
Desative o WebSocket ao usar socket.io no Node.js
Se estiver usando o suporte a WebSocket no socket.io no Node.js, desative o módulo WebSocket do IIS padrão usando o webSocket elemento em web.config ou applicationHost.config. Se esta etapa não for executada, o módulo WebSocket do IIS tentará manipular a comunicação 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 WebSocket, e o servidor reenvia todas as mensagens que recebe de volta para o cliente. O aplicativo de exemplo não está configurado para ser executado a partir do Visual Studio com o IIS Express, portanto, execute o aplicativo em um shell de comando com dotnet run e navegue em um navegador para http://localhost:5000. A página da Web mostra o status da conexão:
Selecione Conectar para enviar uma solicitação WebSocket para a URL mostrada. 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 abrir, enviar e fechar conforme ocorre.