Guia de API dos Hubs do ASP.NET SignalR – Servidor (SignalR 1.x)

por Patrick Fletcher, Tom Dykstra

Aviso

Esta documentação não é para a versão mais recente do SignalR. Dê uma olhada em ASP.NET Core SignalR.

Este documento fornece uma introdução à programação do lado do servidor da API de Hubs do ASP.NET SignalR para SignalR versão 1.1, com exemplos de código demonstrando opções comuns.

A API dos Hubs signalr permite que você faça RPCs (chamadas de procedimento remoto) de um servidor para clientes conectados e de clientes para o servidor. No código do servidor, você define métodos que podem ser chamados por clientes e chama métodos executados no cliente. No código do cliente, você define métodos que podem ser chamados do servidor e chama métodos executados no servidor. O SignalR cuida de todo o encanamento cliente-servidor para você.

O SignalR também oferece uma API de nível inferior chamada Conexões Persistentes. Para obter uma introdução ao SignalR, hubs e conexões persistentes ou para um tutorial que mostra como criar um aplicativo SignalR completo, consulte SignalR - Introdução.

Visão geral

Este documento contém as seguintes seções:

Para obter a documentação sobre como programar clientes, consulte os seguintes recursos:

Os links para tópicos de Referência de API são para a versão do .NET 4.5 da API. Se você estiver usando o .NET 4, consulte a versão do .NET 4 dos tópicos da API.

Como registrar a rota do SignalR e configurar as opções do SignalR

Para definir a rota que os clientes usarão para se conectar ao Hub, chame o método MapHubs quando o aplicativo for iniciado. MapHubs é um método de extensão para a System.Web.Routing.RouteCollection classe . O exemplo a seguir mostra como definir a rota hubs do SignalR no arquivo Global.asax .

using System.Web.Routing;
public class Global : System.Web.HttpApplication
{
    protected void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapHubs();
    }
}

Se você estiver adicionando a funcionalidade do SignalR a um aplicativo MVC ASP.NET, verifique se a rota do SignalR foi adicionada antes das outras rotas. Para obter mais informações, consulte Tutorial: Introdução com SignalR e MVC 4.

A URL /signalr

Por padrão, a URL de rota que os clientes usarão para se conectar ao Hub é "/signalr". (Não confunda essa URL com a URL "/signalr/hubs", que é para o arquivo JavaScript gerado automaticamente. Para obter mais informações sobre o proxy gerado, consulte Guia da API dos Hubs do SignalR – Cliente JavaScript – O proxy gerado e o que ele faz para você.)

Pode haver circunstâncias extraordinárias que tornam essa URL base inutilizável para o SignalR; por exemplo, você tem uma pasta em seu projeto chamada signalr e não deseja alterar o nome. Nesse caso, você pode alterar a URL base, conforme mostrado nos exemplos a seguir (substitua "/signalr" no código de exemplo pela URL desejada).

Código do servidor que especifica a URL

RouteTable.Routes.MapHubs("/signalr", new HubConfiguration());

Código do cliente JavaScript que especifica a URL (com o proxy gerado)

$.connection.hub.url = "/signalr"
$.connection.hub.start().done(init);

Código do cliente JavaScript que especifica a URL (sem o proxy gerado)

var connection = $.hubConnection("/signalr", { useDefaultPath: false });

Código do cliente .NET que especifica a URL

var hubConnection = new HubConnection("http://contoso.com/signalr", useDefaultUrl: false);

Configurando opções do SignalR

As sobrecargas do MapHubs método permitem que você especifique uma URL personalizada, um resolvedor de dependência personalizado e as seguintes opções:

  • Habilitar chamadas entre domínios de clientes do navegador.

    Normalmente, se o navegador carregar uma página de http://contoso.com, a conexão do SignalR estará no mesmo domínio, em http://contoso.com/signalr. Se a página de http://contoso.com fizer uma conexão com http://fabrikam.com/signalr, essa será uma conexão entre domínios. Por motivos de segurança, as conexões entre domínios são desabilitadas por padrão. Para obter mais informações, consulte ASP.NET Guia de API dos Hubs do SignalR – Cliente JavaScript – Como estabelecer uma conexão entre domínios.

  • Habilite mensagens de erro detalhadas.

    Quando ocorrem erros, o comportamento padrão do SignalR é enviar aos clientes uma mensagem de notificação sem detalhes sobre o que aconteceu. O envio de informações detalhadas de erro aos clientes não é recomendado na produção, pois usuários mal-intencionados podem usar as informações em ataques contra seu aplicativo. Para solução de problemas, você pode usar essa opção para habilitar temporariamente relatórios de erros mais informativos.

  • Desabilitar arquivos de proxy JavaScript gerados automaticamente.

    Por padrão, um arquivo JavaScript com proxies para suas classes hub é gerado em resposta à URL "/signalr/hubs". Se você não quiser usar os proxies JavaScript ou se quiser gerar esse arquivo manualmente e se referir a um arquivo físico em seus clientes, poderá usar essa opção para desabilitar a geração de proxy. Para obter mais informações, consulte SignalR Hubs API Guide - JavaScript Client - How to create a physical file for the SignalR generated proxy.

O exemplo a seguir mostra como especificar a URL de conexão do SignalR e essas opções em uma chamada para o MapHubs método . Para especificar uma URL personalizada, substitua "/signalr" no exemplo pela URL que você deseja usar.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableCrossDomain = true;
hubConfiguration.EnableDetailedErrors = true;
hubConfiguration.EnableJavaScriptProxies = false;
RouteTable.Routes.MapHubs("/signalr", hubConfiguration);

Como criar e usar classes hub

Para criar um Hub, crie uma classe derivada de Microsoft.Aspnet.Signalr.Hub. O exemplo a seguir mostra uma classe hub simples para um aplicativo de chat.

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

Neste exemplo, um cliente conectado pode chamar o NewContosoChatMessage método e, quando o faz, os dados recebidos são transmitidos para todos os clientes conectados.

Tempo de vida do objeto hub

Você não cria uma instância da classe Hub nem chama seus métodos do seu próprio código no servidor; tudo o que é feito para você pelo pipeline dos Hubs do SignalR. O SignalR cria uma nova instância da classe Hub sempre que precisa lidar com uma operação de Hub, como quando um cliente se conecta, desconecta ou faz uma chamada de método para o servidor.

Como as instâncias da classe Hub são transitórias, você não pode usá-las para manter o estado de uma chamada de método para a próxima. Sempre que o servidor recebe uma chamada de método de um cliente, uma nova instância da classe Hub processa a mensagem. Para manter o estado por meio de várias conexões e chamadas de método, use algum outro método, como um banco de dados ou uma variável estática na classe Hub, ou uma classe diferente que não deriva de Hub. Se você persistir dados na memória, usando um método como uma variável estática na classe Hub, os dados serão perdidos quando o domínio do aplicativo for reciclado.

Se você quiser enviar mensagens para clientes de seu próprio código executado fora da classe Hub, não poderá fazer isso instanciando uma instância de classe hub, mas pode fazer isso obtendo uma referência ao objeto de contexto SignalR para sua classe Hub. Para obter mais informações, consulte Como chamar métodos de cliente e gerenciar grupos de fora da classe Hub mais adiante neste tópico.

Uso de maiúsculas e minúsculas de nomes de Hub em clientes JavaScript

Por padrão, os clientes JavaScript referem-se aos Hubs usando uma versão com maiúsculas e minúsculas do nome da classe. O SignalR faz essa alteração automaticamente para que o código JavaScript possa estar em conformidade com as convenções do JavaScript. O exemplo anterior seria conhecido como contosoChatHub no código JavaScript.

Servidor

public class ContosoChatHub : Hub

Cliente JavaScript usando proxy gerado

var contosoChatHubProxy = $.connection.contosoChatHub;

Se você quiser especificar um nome diferente para os clientes usarem, adicione o HubName atributo . Quando você usa um HubName atributo, não há nenhuma alteração de nome para o caso camel em clientes JavaScript.

Servidor

[HubName("PascalCaseContosoChatHub")]
public class ContosoChatHub : Hub

Cliente JavaScript usando proxy gerado

var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;

Vários Hubs

Você pode definir várias classes de Hub em um aplicativo. Quando você faz isso, a conexão é compartilhada, mas os grupos são separados:

  • Todos os clientes usarão a mesma URL para estabelecer uma conexão signalr com seu serviço ("/signalr" ou sua URL personalizada, se você especificou um), e essa conexão é usada para todos os Hubs definidos pelo serviço.

    Não há diferença de desempenho para vários Hubs em comparação com a definição de todas as funcionalidades do Hub em uma única classe.

  • Todos os Hubs obtêm as mesmas informações de solicitação HTTP.

    Como todos os Hubs compartilham a mesma conexão, as únicas informações de solicitação HTTP que o servidor obtém é o que vem na solicitação HTTP original que estabelece a conexão SignalR. Se você usar a solicitação de conexão para passar informações do cliente para o servidor especificando uma cadeia de caracteres de consulta, não poderá fornecer cadeias de caracteres de consulta diferentes para hubs diferentes. Todos os Hubs receberão as mesmas informações.

  • O arquivo de proxies JavaScript gerado conterá proxies para todos os Hubs em um arquivo.

    Para obter informações sobre proxies JavaScript, consulte Guia da API dos Hubs do SignalR – Cliente JavaScript – O proxy gerado e o que ele faz por você.

  • Os grupos são definidos em Hubs.

    No SignalR, você pode definir grupos nomeados para difundir para subconjuntos de clientes conectados. Os grupos são mantidos separadamente para cada Hub. Por exemplo, um grupo chamado "Administradores" incluiria um conjunto de clientes para sua ContosoChatHub classe e o mesmo nome de grupo se referiria a um conjunto diferente de clientes para sua StockTickerHub classe.

Como definir métodos na classe Hub que os clientes podem chamar

Para expor um método no Hub que você deseja chamar do cliente, declare um método público, conforme mostrado nos exemplos a seguir.

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}
public class StockTickerHub : Hub
{
    public IEnumerable<Stock> GetAllStocks()
    {
        return _stockTicker.GetAllStocks();
    }
}

Você pode especificar um tipo de retorno e parâmetros, incluindo tipos complexos e matrizes, como faria em qualquer método C#. Todos os dados recebidos em parâmetros ou retornados ao chamador são comunicados entre o cliente e o servidor usando JSON, e o SignalR manipula a associação de objetos complexos e matrizes de objetos automaticamente.

Uso de maiúsculas e minúsculas de nomes de método em clientes JavaScript

Por padrão, os clientes JavaScript referem-se aos métodos hub usando uma versão com maiúsculas e minúsculas do nome do método. O SignalR faz essa alteração automaticamente para que o código JavaScript possa estar em conformidade com as convenções do JavaScript.

Servidor

public void NewContosoChatMessage(string userName, string message)

Cliente JavaScript usando proxy gerado

contosoChatHubProxy.server.newContosoChatMessage($(userName, message);

Se você quiser especificar um nome diferente para os clientes usarem, adicione o HubMethodName atributo .

Servidor

[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)

Cliente JavaScript usando proxy gerado

contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

Quando executar de forma assíncrona

Se o método for de execução prolongada ou tiver que fazer um trabalho que envolva espera, como uma pesquisa de banco de dados ou uma chamada de serviço Web, torne o método Hub assíncrono retornando uma Tarefa (no lugar do void retorno) ou um objeto Task<T> (no lugar do tipo de T retorno). Quando você retorna um Task objeto do método , o SignalR aguarda a Task conclusão e envia o resultado desembrulhado de volta para o cliente, portanto, não há diferença na forma como você codifica a chamada de método no cliente.

Tornar um método hub assíncrono evita bloquear a conexão quando ele usa o transporte WebSocket. Quando um método Hub é executado de forma síncrona e o transporte é WebSocket, as invocações subsequentes de métodos no Hub do mesmo cliente são bloqueadas até que o método Hub seja concluído.

O exemplo a seguir mostra o mesmo método codificado para ser executado de forma síncrona ou assíncrona, seguido pelo código do cliente JavaScript que funciona para chamar qualquer versão.

Síncrono

public IEnumerable<Stock> GetAllStocks()
{
    // Returns data from memory.
    return _stockTicker.GetAllStocks();
}

Assíncrono - ASP.NET 4.5

public async Task<IEnumerable<Stock>> GetAllStocks()
{
    // Returns data from a web service.
    var uri = Util.getServiceUri("Stocks");
    using (HttpClient httpClient = new HttpClient())
    {
        var response = await httpClient.GetAsync(uri);
        return (await response.Content.ReadAsAsync<IEnumerable<Stock>>());
    }
}

Cliente JavaScript usando proxy gerado

stockTickerHubProxy.server.getAllStocks().done(function (stocks) {
    $.each(stocks, function () {
        alert(this.Symbol + ' ' + this.Price);
    });
});

Para obter mais informações sobre como usar métodos assíncronos no ASP.NET 4.5, consulte Usando métodos assíncronos no ASP.NET MVC 4.

Definindo sobrecargas

Se você quiser definir sobrecargas para um método, o número de parâmetros em cada sobrecarga deve ser diferente. Se você diferenciar uma sobrecarga apenas especificando diferentes tipos de parâmetro, sua classe Hub será compilada, mas o serviço SignalR lançará uma exceção em tempo de execução quando os clientes tentarem chamar uma das sobrecargas.

Como chamar métodos de cliente da classe Hub

Para chamar métodos de cliente do servidor, use a Clients propriedade em um método em sua classe Hub. O exemplo a seguir mostra o código do servidor que chama addNewMessageToPage todos os clientes conectados e o código do cliente que define o método em um cliente JavaScript.

Servidor

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

Cliente JavaScript usando proxy gerado

contosoChatHubProxy.client.addNewMessageToPage = function (name, message) {
    // Add the message to the page. 
    $('#discussion').append('<li><strong>' + htmlEncode(name)
        + '</strong>: ' + htmlEncode(message) + '<li>');
};

Você não pode obter um valor retornado de um método cliente; sintaxe como int x = Clients.All.add(1,1) não funciona.

Você pode especificar tipos complexos e matrizes para os parâmetros. O exemplo a seguir passa um tipo complexo para o cliente em um parâmetro de método.

Código do servidor que chama um método cliente usando um objeto complexo

public void SendMessage(string name, string message)
{
    Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}

Código do servidor que define o objeto complexo

public class ContosoChatMessage
{
    public string UserName { get; set; }
    public string Message { get; set; }
}

Cliente JavaScript usando proxy gerado

var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
    console.log(message.UserName + ' ' + message.Message);
});

Selecionando quais clientes receberão o RPC

A propriedade Clients retorna um objeto HubConnectionContext que fornece várias opções para especificar quais clientes receberão o RPC:

  • Todos os clientes conectados.

    Clients.All.addContosoChatMessageToPage(name, message);
    
  • Somente o cliente de chamada.

    Clients.Caller.addContosoChatMessageToPage(name, message);
    
  • Todos os clientes, exceto o cliente de chamada.

    Clients.Others.addContosoChatMessageToPage(name, message);
    
  • Um cliente específico identificado pela ID de conexão.

    Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);
    

    Este exemplo chama addContosoChatMessageToPage o cliente de chamada e tem o mesmo efeito que usar Clients.Caller.

  • Todos os clientes conectados, exceto os clientes especificados, identificados pela ID de conexão.

    Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Todos os clientes conectados em um grupo especificado.

    Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • Todos os clientes conectados em um grupo especificado, exceto os clientes especificados, identificados pela ID de conexão.

    Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Todos os clientes conectados em um grupo especificado, exceto o cliente de chamada.

    Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);
    

Nenhuma validação em tempo de compilação para nomes de método

O nome do método especificado é interpretado como um objeto dinâmico, o que significa que não há nenhum IntelliSense ou validação em tempo de compilação para ele. A expressão é avaliada em tempo de execução. Quando a chamada de método é executada, o SignalR envia o nome do método e os valores de parâmetro para o cliente e, se o cliente tiver um método que corresponda ao nome, esse método será chamado e os valores de parâmetro serão passados para ele. Se nenhum método correspondente for encontrado no cliente, nenhum erro será gerado. Para obter informações sobre o formato dos dados que o SignalR transmite para o cliente nos bastidores quando você chama um método cliente, consulte Introdução ao SignalR.

Correspondência de nome de método que não diferencia maiúsculas de minúsculas

A correspondência de nome de método não diferencia maiúsculas de minúsculas. Por exemplo, Clients.All.addContosoChatMessageToPage no servidor executará AddContosoChatMessageToPage, addcontosochatmessagetopageou addContosoChatMessageToPage no cliente.

Execução assíncrona

O método que você chama é executado de forma assíncrona. Qualquer código que venha após uma chamada de método para um cliente será executado imediatamente sem esperar que o SignalR conclua a transmissão de dados para clientes, a menos que você especifique que as linhas de código subsequentes devem aguardar a conclusão do método. Os exemplos de código a seguir mostram como executar dois métodos de cliente sequencialmente, um usando código que funciona no .NET 4.5 e outro usando código que funciona no .NET 4.

Exemplo do .NET 4.5

async public Task NewContosoChatMessage(string name, string message)
{
    await Clients.Others.addContosoChatMessageToPage(data);
    Clients.Caller.notifyMessageSent();
}

Exemplo do .NET 4

public void NewContosoChatMessage(string name, string message)
{
    (Clients.Others.addContosoChatMessageToPage(data) as Task).ContinueWith(antecedent =>
        Clients.Caller.notifyMessageSent());
}

Se você usar await ou ContinueWith aguardar até que um método cliente seja concluído antes da próxima linha de código ser executada, isso não significa que os clientes realmente receberão a mensagem antes que a próxima linha de código seja executada. "Conclusão" de uma chamada de método de cliente significa apenas que o SignalR fez tudo o que era necessário para enviar a mensagem. Se você precisar de verificação de que os clientes receberam a mensagem, você precisa programar esse mecanismo por conta própria. Por exemplo, você pode codificar um MessageReceived método no Hub e, no addContosoChatMessageToPage método no cliente, você pode chamar MessageReceived depois de fazer qualquer trabalho necessário no cliente. No MessageReceived Hub, você pode fazer qualquer trabalho que dependa da recepção real do cliente e do processamento da chamada de método original.

Como usar uma variável de cadeia de caracteres como o nome do método

Se você quiser invocar um método cliente usando uma variável de cadeia de caracteres como o nome do método, converta Clients.All (ou Clients.Others, Clients.Calleretc.) para IClientProxy e chame Invoke(methodName, args...).

public void NewContosoChatMessage(string name, string message)
{
    string methodToCall = "addContosoChatMessageToPage";
    IClientProxy proxy = Clients.All;
    proxy.Invoke(methodToCall, name, message);
}

Como gerenciar a associação de grupo da classe Hub

Os grupos no SignalR fornecem um método para transmitir mensagens para subconjuntos especificados de clientes conectados. Um grupo pode ter qualquer número de clientes e um cliente pode ser membro de qualquer número de grupos.

Para gerenciar a associação de grupo, use os métodos Adicionar e Remover fornecidos pela Groups propriedade da classe Hub. O exemplo a seguir mostra os Groups.Add métodos e Groups.Remove usados em métodos hub que são chamados pelo código do cliente, seguidos pelo código do cliente JavaScript que os chama.

Servidor

public class ContosoChatHub : Hub
{
    public Task JoinGroup(string groupName)
    {
        return Groups.Add(Context.ConnectionId, groupName);
    }

    public Task LeaveGroup(string groupName)
    {
        return Groups.Remove(Context.ConnectionId, groupName);
    }
}

Cliente JavaScript usando proxy gerado

contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName);

Você não precisa criar grupos explicitamente. Na verdade, um grupo é criado automaticamente na primeira vez que você especifica seu nome em uma chamada para Groups.Adde é excluído quando você remove a última conexão da associação nele.

Não há nenhuma API para obter uma lista de associação de grupo ou uma lista de grupos. O SignalR envia mensagens para clientes e grupos com base em um modelo pub/sub e o servidor não mantém listas de grupos ou associações de grupo. Isso ajuda a maximizar a escalabilidade, pois sempre que você adiciona um nó a um web farm, qualquer estado que o SignalR mantém precisa ser propagado para o novo nó.

Execução assíncrona dos métodos Add e Remove

Os Groups.Add métodos e Groups.Remove são executados de forma assíncrona. Se você quiser adicionar um cliente a um grupo e enviar imediatamente uma mensagem ao cliente usando o grupo , precisará garantir que o Groups.Add método seja concluído primeiro. Os exemplos de código a seguir mostram como fazer isso, um usando código que funciona no .NET 4.5 e outro usando código que funciona no .NET 4

Exemplo do .NET 4.5

async public Task JoinGroup(string groupName)
{
    await Groups.Add(Context.ConnectionId, groupName);
    Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group");
}

Exemplo do .NET 4

public void JoinGroup(string groupName)
{
    (Groups.Add(Context.ConnectionId, groupName) as Task).ContinueWith(antecedent =>
        Clients.Group(groupName).addContosoChatMessageToPage(Context.ConnectionId + " added to group"));
}

Persistência de associação de grupo

O SignalR rastreia conexões, não usuários, portanto, se você quiser que um usuário esteja no mesmo grupo sempre que o usuário estabelecer uma conexão, será necessário chamar Groups.Add sempre que o usuário estabelecer uma nova conexão.

Após uma perda temporária de conectividade, às vezes o SignalR pode restaurar a conexão automaticamente. Nesse caso, o SignalR está restaurando a mesma conexão, não estabelecendo uma nova conexão e, portanto, a associação de grupo do cliente é restaurada automaticamente. Isso é possível mesmo quando a interrupção temporária é o resultado de uma reinicialização ou falha do servidor, pois o estado de conexão para cada cliente, incluindo associações de grupo, é arredondado para o cliente. Se um servidor ficar inativo e for substituído por um novo servidor antes do tempo limite da conexão, um cliente poderá se reconectar automaticamente ao novo servidor e se registrar novamente em grupos dos quais ele é membro.

Quando uma conexão não pode ser restaurada automaticamente após uma perda de conectividade ou quando a conexão atinge o tempo limite ou quando o cliente se desconecta (por exemplo, quando um navegador navega para uma nova página), as associações de grupo são perdidas. A próxima vez que o usuário se conectar será uma nova conexão. Para manter associações de grupo quando o mesmo usuário estabelece uma nova conexão, seu aplicativo precisa acompanhar as associações entre usuários e grupos e restaurar associações de grupo sempre que um usuário estabelecer uma nova conexão.

Para obter mais informações sobre conexões e reconexões, consulte Como lidar com eventos de tempo de vida da conexão na classe Hub mais adiante neste tópico.

Grupos de usuário único

Os aplicativos que usam o SignalR normalmente precisam acompanhar as associações entre usuários e conexões para saber qual usuário enviou uma mensagem e quais usuários devem receber uma mensagem. Os grupos são usados em um dos dois padrões comumente usados para fazer isso.

  • Grupos de usuário único.

    Você pode especificar o nome de usuário como o nome de grupo e adicionar a ID de conexão atual ao grupo sempre que o usuário se conectar ou se reconectar. Para enviar mensagens para o usuário que você envia para o grupo. Uma desvantagem desse método é que o grupo não fornece uma maneira de descobrir se o usuário está online ou offline.

  • Acompanhe as associações entre nomes de usuário e IDs de conexão.

    Você pode armazenar uma associação entre cada nome de usuário e uma ou mais IDs de conexão em um dicionário ou banco de dados e atualizar os dados armazenados sempre que o usuário se conectar ou desconectar. Para enviar mensagens ao usuário, especifique as IDs de conexão. Uma desvantagem desse método é que ele usa mais memória.

Como lidar com eventos de tempo de vida da conexão na classe Hub

Os motivos típicos para lidar com eventos de tempo de vida da conexão são controlar se um usuário está conectado ou não e acompanhar a associação entre nomes de usuário e IDs de conexão. Para executar seu próprio código quando os clientes se conectarem ou se desconectarem, substitua os OnConnectedmétodos virtuais , OnDisconnectede OnReconnected da classe Hub, conforme mostrado no exemplo a seguir.

public class ContosoChatHub : Hub
{
    public override Task OnConnected()
    {
        // Add your own code here.
        // For example: in a chat application, record the association between
        // the current connection ID and user name, and mark the user as online.
        // After the code in this method completes, the client is informed that
        // the connection is established; for example, in a JavaScript client,
        // the start().done callback is executed.
        return base.OnConnected();
    }

    public override Task OnDisconnected()
    {
        // Add your own code here.
        // For example: in a chat application, mark the user as offline, 
        // delete the association between the current connection id and user name.
        return base.OnDisconnected();
    }

    public override Task OnReconnected()
    {
        // Add your own code here.
        // For example: in a chat application, you might have marked the
        // user as offline after a period of inactivity; in that case 
        // mark the user as online again.
        return base.OnReconnected();
    }
}

Quando OnConnected, OnDisconnected e OnReconnected são chamados

Cada vez que um navegador navega para uma nova página, uma nova conexão precisa ser estabelecida, o que significa que o SignalR executará o OnDisconnected método seguido pelo OnConnected método . O SignalR sempre cria uma nova ID de conexão quando uma nova conexão é estabelecida.

O OnReconnected método é chamado quando há uma interrupção temporária na conectividade da qual o SignalR pode se recuperar automaticamente, como quando um cabo é temporariamente desconectado e reconectado antes do tempo limite da conexão. O OnDisconnected método é chamado quando o cliente é desconectado e o SignalR não pode se reconectar automaticamente, como quando um navegador navega para uma nova página. Portanto, uma possível sequência de eventos para um determinado cliente é OnConnected, OnReconnected, OnDisconnectedou OnConnected. OnDisconnected Você não verá a sequência OnConnected, OnDisconnected, OnReconnected para uma determinada conexão.

O OnDisconnected método não é chamado em alguns cenários, como quando um servidor fica inativo ou o Domínio do Aplicativo é reciclado. Quando outro servidor entra em linha ou o Domínio do Aplicativo conclui sua reciclagem, alguns clientes podem ser capazes de reconectar e disparar o OnReconnected evento.

Para obter mais informações, consulte Noções básicas e tratamento de eventos de tempo de vida da conexão no SignalR.

Estado do chamador não preenchido

Os métodos do manipulador de eventos de tempo de vida da conexão são chamados do servidor, o que significa que qualquer estado que você colocar no state objeto no cliente não será preenchido na Caller propriedade no servidor. Para obter informações sobre o state objeto e a Caller propriedade , consulte Como passar o estado entre clientes e a classe Hub mais adiante neste tópico.

Como obter informações sobre o cliente da propriedade Context

Para obter informações sobre o cliente, use a Context propriedade da classe Hub. A Context propriedade retorna um objeto HubCallerContext que fornece acesso às seguintes informações:

  • A ID de conexão do cliente de chamada.

    string connectionID = Context.ConnectionId;
    

    A ID de conexão é um GUID atribuído pelo SignalR (você não pode especificar o valor em seu próprio código). Há uma ID de conexão para cada conexão e a mesma ID de conexão é usada por todos os Hubs se você tiver vários Hubs em seu aplicativo.

  • Dados de cabeçalho HTTP.

    System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;
    

    Você também pode obter cabeçalhos HTTP de Context.Headers. O motivo para várias referências à mesma coisa é que Context.Headers foi criado primeiro, a Context.Request propriedade foi adicionada posteriormente e Context.Headers foi mantida para compatibilidade com versões anteriores.

  • Consultar dados de cadeia de caracteres.

    System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString;
    string parameterValue = queryString["parametername"]
    

    Você também pode obter dados de cadeia de caracteres de consulta de Context.QueryString.

    A cadeia de caracteres de consulta que você obtém nessa propriedade é aquela que foi usada com a solicitação HTTP que estabeleceu a conexão do SignalR. Você pode adicionar parâmetros de cadeia de caracteres de consulta no cliente configurando a conexão, que é uma maneira conveniente de passar dados sobre o cliente do cliente para o servidor. O exemplo a seguir mostra uma maneira de adicionar uma cadeia de caracteres de consulta em um cliente JavaScript quando você usa o proxy gerado.

    $.connection.hub.qs = { "version" : "1.0" };
    

    Para obter mais informações sobre como definir parâmetros de cadeia de caracteres de consulta, consulte os guias de API para os clientes JavaScript e .NET .

    Você pode encontrar o método de transporte usado para a conexão nos dados da cadeia de caracteres de consulta, juntamente com alguns outros valores usados internamente pelo SignalR:

    string transportMethod = queryString["transport"];
    

    O valor de transportMethod será "webSockets", "serverSentEvents", "foreverFrame" ou "longPolling". Observe que, se você marcar esse valor no método do OnConnected manipulador de eventos, em alguns cenários, você poderá obter inicialmente um valor de transporte que não é o método de transporte negociado final para a conexão. Nesse caso, o método lançará uma exceção e será chamado novamente mais tarde quando o método de transporte final for estabelecido.

  • Cookies.

    System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies;
    

    Você também pode obter cookies de Context.RequestCookies.

  • Informação do usuário.

    System.Security.Principal.IPrincipal user = Context.User;
    
  • O objeto HttpContext para a solicitação :

    System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();
    

    Use esse método em vez de obter HttpContext.Current o HttpContext objeto para a conexão do SignalR.

Como passar o estado entre clientes e a classe Hub

O proxy do cliente fornece um state objeto no qual você pode armazenar dados que você deseja transmitir para o servidor com cada chamada de método. No servidor, você pode acessar esses dados na Clients.Caller propriedade em métodos hub que são chamados por clientes. A Clients.Caller propriedade não é preenchida para os métodos OnConnecteddo manipulador de eventos de tempo de vida da conexão , OnDisconnectede OnReconnected.

Criar ou atualizar dados no state objeto e a Clients.Caller propriedade funciona em ambas as direções. Você pode atualizar os valores no servidor e eles são passados de volta para o cliente.

O exemplo a seguir mostra o código do cliente JavaScript que armazena o estado para transmissão para o servidor com cada chamada de método.

contosoChatHubProxy.state.userName = "Fadi Fakhouri";
contosoChatHubProxy.state.computerName = "fadivm1";

O exemplo a seguir mostra o código equivalente em um cliente .NET.

contosoChatHubProxy["userName"] = "Fadi Fakhouri";
chatHubProxy["computerName"] = "fadivm1";

Na classe Hub, você pode acessar esses dados na Clients.Caller propriedade . O exemplo a seguir mostra o código que recupera o estado referenciado no exemplo anterior.

public void NewContosoChatMessage(string data)
{
    string userName = Clients.Caller.userName;
    string computerName = Clients.Caller.computerName;
    Clients.Others.addContosoChatMessageToPage(message, userName, computerName);
}

Observação

Esse mecanismo para manter o estado não se destina a grandes quantidades de dados, pois tudo o state que você coloca na propriedade ou Clients.Caller é arredondado com cada invocação de método. Ele é útil para itens menores, como nomes de usuário ou contadores.

Como lidar com erros na classe Hub

Para lidar com erros que ocorrem em seus métodos de classe hub, use um ou ambos os seguintes métodos:

  • Encapsule o código do método em blocos try-catch e registre o objeto de exceção. Para fins de depuração, você pode enviar a exceção ao cliente, mas, por motivos de segurança, não é recomendável enviar informações detalhadas aos clientes em produção.

  • Crie um módulo de pipeline de Hubs que manipula o método OnIncomingError . O exemplo a seguir mostra um módulo de pipeline que registra erros, seguido pelo código em Global.asax que injeta o módulo no pipeline de Hubs.

    public class ErrorHandlingPipelineModule : HubPipelineModule
    {
        protected override void OnIncomingError(Exception ex, IHubIncomingInvokerContext context)
        {
            Debug.WriteLine("=> Exception " + ex.Message);
            if (ex.InnerException != null)
            {
                Debug.WriteLine("=> Inner Exception " + ex.InnerException.Message);
            }
            base.OnIncomingError(ex, context);
        }
    }
    
    protected void Application_Start(object sender, EventArgs e)
    {
        GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); 
        RouteTable.Routes.MapHubs();
    }
    

Para obter mais informações sobre os módulos de pipeline do Hub, consulte Como personalizar o pipeline de Hubs mais adiante neste tópico.

Como habilitar o rastreamento

Para habilitar o rastreamento do lado do servidor, adicione um sistema. diagnóstico elemento ao arquivo Web.config, conforme mostrado neste exemplo:

<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit https://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="SignalRSamples" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;" />
    <add name="SignalRSamplesWithMARS" connectionString="Data Source=(local);Initial Catalog=SignalRSamples;Integrated Security=SSPI;Asynchronous Processing=True;MultipleActiveResultSets=true;" />
  </connectionStrings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
  </system.webServer>
  <system.diagnostics>
    <sources>
      <source name="SignalR.SqlMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
     <source name="SignalR.ServiceBusMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
     </source>
     <source name="SignalR.ScaleoutMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.Transports.WebSocketTransport">
        <listeners>
          <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.ServerSentEventsTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.ForeverFrameTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.LongPollingTransport">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.TransportHeartBeat">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="SignalRSwitch" value="Verbose" />
    </switches>
    <sharedListeners>
      <add name="SignalR-Transports" 
           type="System.Diagnostics.TextWriterTraceListener" 
           initializeData="transports.log.txt" />
        <add name="SignalR-Bus"
           type="System.Diagnostics.TextWriterTraceListener"
           initializeData="bus.log.txt" />
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
  </entityFramework>
</configuration>

Ao executar o aplicativo no Visual Studio, você pode exibir os logs na janela Saída .

Como chamar métodos de cliente e gerenciar grupos de fora da classe Hub

Para chamar métodos de cliente de uma classe diferente da classe Hub, obtenha uma referência ao objeto de contexto SignalR para o Hub e use-a para chamar métodos no cliente ou gerenciar grupos.

A classe de exemplo StockTicker a seguir obtém o objeto de contexto, armazena-o em uma instância da classe , armazena a instância de classe em uma propriedade estática e usa o contexto da instância de classe singleton para chamar o updateStockPrice método em clientes conectados a um Hub chamado StockTickerHub.

// For the complete example, go to 
// http://www.asp.net/signalr/overview/signalr-1x/getting-started/tutorial-server-broadcast-with-aspnet-signalr
// This sample only shows code related to getting and using the SignalR context.
public class StockTicker
{
    // Singleton instance
    private readonly static Lazy<StockTicker> _instance = new Lazy<StockTicker>(
        () => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>()));

    private IHubContext _context;

    private StockTicker(IHubContext context)
    {
        _context = context;
    }

    // This method is invoked by a Timer object.
    private void UpdateStockPrices(object state)
    {
        foreach (var stock in _stocks.Values)
        {
            if (TryUpdateStockPrice(stock))
            {
                _context.Clients.All.updateStockPrice(stock);
            }
        }
    }

Se você precisar usar o contexto várias vezes em um objeto de longa duração, obtenha a referência uma vez e salve-a em vez de obtê-la novamente a cada vez. Obter o contexto uma vez garante que o SignalR envie mensagens para clientes na mesma sequência em que os métodos hub fazem invocações de método de cliente. Para obter um tutorial que mostra como usar o contexto do SignalR para um Hub, consulte Transmissão de servidor com ASP.NET SignalR.

Chamando métodos de cliente

Você pode especificar quais clientes receberão o RPC, mas você tem menos opções do que quando chama de uma classe hub. O motivo disso é que o contexto não está associado a uma chamada específica de um cliente, portanto, todos os métodos que exigem conhecimento da ID de conexão atual, como Clients.Others, ou Clients.Callerou Clients.OthersInGroup, não estão disponíveis. As seguintes opções estão disponíveis:

  • Todos os clientes conectados.

    context.Clients.All.addContosoChatMessageToPage(name, message);
    
  • Um cliente específico identificado pela ID de conexão.

    context.Clients.Client(connectionID).addContosoChatMessageToPage(name, message);
    
  • Todos os clientes conectados, exceto os clientes especificados, identificados pela ID de conexão.

    context.Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Todos os clientes conectados em um grupo especificado.

    context.Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • Todos os clientes conectados em um grupo especificado, exceto clientes especificados, identificados pela ID de conexão.

    Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    

Se você estiver chamando sua classe não Hub de métodos em sua classe Hub, poderá passar a ID de conexão atual e usá-la com Clients.Client, Clients.AllExceptou Clients.Group para simular Clients.Caller, Clients.Othersou Clients.OthersInGroup. No exemplo a seguir, a MoveShapeHub classe passa a ID de conexão para a Broadcaster classe para que a Broadcaster classe possa simular Clients.Others.

// For the complete example, see
// http://www.asp.net/signalr/overview/getting-started/tutorial-high-frequency-realtime-with-signalr
// This sample only shows code that passes connection ID to the non-Hub class,
// in order to simulate Clients.Others.
public class MoveShapeHub : Hub
{
    // Code not shown puts a singleton instance of Broadcaster in this variable.
    private Broadcaster _broadcaster;

    public void UpdateModel(ShapeModel clientModel)
    {
        clientModel.LastUpdatedBy = Context.ConnectionId;
        // Update the shape model within our broadcaster
        _broadcaster.UpdateShape(clientModel);
    }
}

public class Broadcaster
{
    public Broadcaster()
    {
        _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
    }

    public void UpdateShape(ShapeModel clientModel)
    {
        _model = clientModel;
        _modelUpdated = true;
    }

    // Called by a Timer object.
    public void BroadcastShape(object state)
    {
        if (_modelUpdated)
        {
            _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
            _modelUpdated = false;
        }
    }
}

Gerenciando a associação ao grupo

Para gerenciar grupos, você tem as mesmas opções que em uma classe Hub.

  • Adicionar um cliente a um grupo

    context.Groups.Add(connectionID, groupName);
    
  • Remover um cliente de um grupo

    context.Groups.Remove(connectionID, groupName);
    

Como personalizar o pipeline hubs

O SignalR permite injetar seu próprio código no pipeline do Hub. O exemplo a seguir mostra um módulo de pipeline do Hub personalizado que registra cada chamada de método de entrada recebida da chamada de método de saída e cliente invocada no cliente:

public class LoggingPipelineModule : HubPipelineModule 
{ 
    protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context) 
    { 
        Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name); 
        return base.OnBeforeIncoming(context); 
    }   
    protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context) 
    { 
        Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub); 
        return base.OnBeforeOutgoing(context); 
    } 
}

O seguinte código no arquivo Global.asax registra o módulo a ser executado no pipeline do Hub:

protected void Application_Start(object sender, EventArgs e) 
{ 
    GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule()); 
    RouteTable.Routes.MapHubs();
}

Há muitos métodos diferentes que você pode substituir. Para obter uma lista completa, consulte Métodos HubPipelineModule.