Suporte ao WebSockets no ASP.NET Core
Observação
Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para informações sobre a versão vigente, confira a Versão do .NET 8 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:
- Compactaçã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 o suporte do WebSockets por HTTP/2 a Kestrel, cliente JavaScript SignalR e SignalR com Blazor WebAssembly.
Observação
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.
O Chrome e o Edge têm WebSockets HTTP/2 habilitados por padrão e você pode habilitá-los no FireFox na página about:config
com o sinalizador network.http.spdy.websockets
.
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 o fallback de transporte para os ambientes em que o WebSocket não está disponível.
- Fornece um modelo de aplicativo de chamada de procedimento remoto básico.
- 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.
Pré-requisitos
- 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 – a frequência para enviar quadros "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 uma fase posterior 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 o suporte do WebSockets por HTTP/2 a Kestrel, 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;
}
}
Compactação
Aviso
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 em Extensões de Compactação para WebSocket RFC.
Observação
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.
Tratar desconexões de cliente
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas quando o cliente a envia, e isso é possível somente quando há conexão com a Internet. 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 não envia mensagens com frequência, e você não deseja atingir um tempo limite apenas porque a conexão fica ociosa, peça ao cliente para usar um temporizador a fim de 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:
- Executam solicitações de simulação de CORS.
- Respeitam as restrições especificadas em cabeçalhos
Access-Control
ao fazer solicitações de 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);
Observação
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.
Observação
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:
Observação
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 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 o Protocolo WebSocket. Selecione Avançar.
- 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:
Observação
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.
Desabilite o WebSocket ao usar o socket.io no Node.js
Se estiver usando o suporte para 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. Caso contrário, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez do Node.js e do 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 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 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:
- Compactaçã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 o suporte do WebSockets por HTTP/2 a Kestrel, cliente JavaScript SignalR e SignalR com Blazor WebAssembly.
Observação
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.
O Chrome e o Edge têm WebSockets HTTP/2 habilitados por padrão e você pode habilitá-los no FireFox na página about:config
com o sinalizador network.http.spdy.websockets
.
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 o fallback de transporte para os ambientes em que o WebSocket não está disponível.
- Fornece um modelo de aplicativo de chamada de procedimento remoto básico.
- 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.
Pré-requisitos
- 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 – a frequência para enviar quadros "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 uma fase posterior 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 o suporte do WebSockets por HTTP/2 a Kestrel, 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;
}
}
Compactação
Aviso
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 em Extensões de Compactação para WebSocket RFC.
Observação
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.
Tratar desconexões de cliente
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas quando o cliente a envia, e isso é possível somente quando há conexão com a Internet. 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 não envia mensagens com frequência, e você não deseja atingir um tempo limite apenas porque a conexão fica ociosa, peça ao cliente para usar um temporizador a fim de 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:
- Executam solicitações de simulação de CORS.
- Respeitam as restrições especificadas em cabeçalhos
Access-Control
ao fazer solicitações de 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);
Observação
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.
Observação
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:
Observação
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 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 o Protocolo WebSocket. Selecione Avançar.
- 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:
Observação
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.
Desabilite o WebSocket ao usar o socket.io no Node.js
Se estiver usando o suporte para 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. Caso contrário, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez do Node.js e do 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 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 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 ao invés de WebSockets brutos. SignalR fornece o fallback de transporte para os ambientes em que os WebSockets não estão disponíveis. Fornece também um modelo de aplicativo de chamada de procedimento remoto básico. 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.
Pré-requisitos
- 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 – a frequência para enviar quadros "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 uma fase posterior 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
.
Compactação
Aviso
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 em Extensões de Compactação para WebSocket RFC.
Observação
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.
Tratar desconexões de cliente
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas quando o cliente a envia, e isso é possível somente quando há conexão com a Internet. 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 não envia mensagens com frequência, e você não deseja atingir um tempo limite apenas porque a conexão fica ociosa, peça ao cliente para usar um temporizador a fim de 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:
- Executam solicitações de simulação de CORS.
- Respeitam as restrições especificadas em cabeçalhos
Access-Control
ao fazer solicitações de 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);
Observação
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.
Observação
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:
Observação
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 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 o Protocolo WebSocket. Selecione Avançar.
- 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:
Observação
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.
Desabilite o WebSocket ao usar o socket.io no Node.js
Se estiver usando o suporte para 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. Caso contrário, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez do Node.js e do 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 ao invés de WebSockets brutos. SignalR fornece o fallback de transporte para os ambientes em que os WebSockets não estão disponíveis. Fornece também um modelo de aplicativo de chamada de procedimento remoto básico. 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.
Pré-requisitos
- 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();
Observação
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 – a frequência para enviar quadros "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.
Tratar desconexões de cliente
O servidor não é informado automaticamente quando o cliente se desconecta devido à perda de conectividade. O servidor recebe uma mensagem de desconexão apenas quando o cliente a envia, e isso é possível somente quando há conexão com a Internet. 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 não envia mensagens com frequência, e você não deseja atingir um tempo limite apenas porque a conexão fica ociosa, peça ao cliente para usar um temporizador a fim de 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.
Observação
O interno ManagedWebSocket
manipula os quadros ping/pong implicitamente para manter a conexão ativa se a opção KeepAliveInterval
for maior que zero, o que significa 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:
- Executam solicitações de simulação de CORS.
- Respeitam as restrições especificadas em cabeçalhos
Access-Control
ao fazer solicitações de 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);
Observação
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.
Observação
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:
Observação
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 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 o Protocolo WebSocket. Selecione Avançar.
- 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:
Observação
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.
Desabilite o WebSocket ao usar o socket.io no Node.js
Se estiver usando o suporte para 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. Caso contrário, o módulo WebSocket do IIS tentará lidar com a comunicação do WebSocket em vez do Node.js e do 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.