ASP.NET Guida api hub SignalR - Server (SignalR 1.x)

di Patrick Fletcher, Tom Dykstra

Avviso

Questa documentazione non è per la versione più recente di SignalR. Esaminare ASP.NET Core SignalR.

Questo documento fornisce un'introduzione alla programmazione del lato server dell'API Hub SignalR ASP.NET per SignalR versione 1.1, con esempi di codice che illustrano le opzioni comuni.

L'API SignalR Hubs consente di effettuare chiamate di procedura remota da un server ai client connessi e dai client al server. Nel codice del server si definiscono i metodi che possono essere chiamati dai client e si chiamano i metodi eseguiti nel client. Nel codice client si definiscono i metodi che possono essere chiamati dal server e si chiamano i metodi eseguiti nel server. SignalR si occupa automaticamente di tutte le operazioni di idraulica da client a server.

SignalR offre anche un'API di livello inferiore denominata Connessioni persistenti. Per un'introduzione a SignalR, Hub e Connessioni permanenti o per un'esercitazione che illustra come compilare un'applicazione SignalR completa, vedere SignalR - Introduzione.

Panoramica

Questo documento contiene le seguenti sezioni:

Per la documentazione su come programmare i client, vedere le risorse seguenti:

I collegamenti agli argomenti di riferimento sulle API sono alla versione .NET 4.5 dell'API. Se si usa .NET 4, vedere la versione .NET 4 degli argomenti dell'API.

Come registrare la route SignalR e configurare le opzioni di SignalR

Per definire la route che i client useranno per connettersi all'hub, chiamare il metodo MapHubs all'avvio dell'applicazione. MapHubs è un metodo di estensione per la System.Web.Routing.RouteCollection classe . L'esempio seguente illustra come definire la route di Hub SignalR nel file Global.asax .

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

Se si aggiunge la funzionalità SignalR a un'applicazione MVC ASP.NET, assicurarsi che la route SignalR venga aggiunta prima delle altre route. Per altre informazioni, vedere Esercitazione: Introduzione con SignalR e MVC 4.

The /signalr URL

Per impostazione predefinita, l'URL di route che i client useranno per connettersi all'hub è "/signalr". (Non confondere questo URL con l'URL "/signalr/hubs", che è per il file JavaScript generato automaticamente. Per altre informazioni sul proxy generato, vedere Guida all'API di SignalR Hubs - Client JavaScript - Proxy generato e cosa fa per l'utente.

Potrebbero esserci circostanze straordinarie che rendono questo URL di base non utilizzabile per SignalR; Ad esempio, si dispone di una cartella nel progetto denominato signalr e non si vuole modificare il nome. In questo caso, è possibile modificare l'URL di base, come illustrato negli esempi seguenti (sostituire "/signalr" nel codice di esempio con l'URL desiderato).

Codice server che specifica l'URL

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

Codice client JavaScript che specifica l'URL (con il proxy generato)

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

Codice client JavaScript che specifica l'URL (senza il proxy generato)

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

Codice client .NET che specifica l'URL

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

Configurazione delle opzioni di SignalR

Gli overload del MapHubs metodo consentono di specificare un URL personalizzato, un resolver di dipendenze personalizzato e le opzioni seguenti:

Nell'esempio seguente viene illustrato come specificare l'URL di connessione SignalR e queste opzioni in una chiamata al MapHubs metodo . Per specificare un URL personalizzato, sostituire "/signalr" nell'esempio con l'URL che si vuole usare.

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

Come creare e usare classi hub

Per creare un hub, creare una classe che deriva da Microsoft.Aspnet.Signalr.Hub. L'esempio seguente illustra una semplice classe Hub per un'applicazione di chat.

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

In questo esempio un client connesso può chiamare il NewContosoChatMessage metodo e, quando lo fa, i dati ricevuti vengono trasmessi a tutti i client connessi.

Durata dell'oggetto hub

Non si crea un'istanza della classe Hub o si chiamano i relativi metodi dal proprio codice nel server; tutto ciò viene eseguito dall'utente dalla pipeline di Hub SignalR. SignalR crea una nuova istanza della classe Hub ogni volta che deve gestire un'operazione hub, ad esempio quando un client si connette, si disconnette o effettua una chiamata al metodo al server.

Poiché le istanze della classe Hub sono temporanee, non è possibile usarle per mantenere lo stato da una chiamata di metodo alla successiva. Ogni volta che il server riceve una chiamata al metodo da un client, una nuova istanza della classe Hub elabora il messaggio. Per mantenere lo stato tramite più connessioni e chiamate al metodo, usare un altro metodo, ad esempio un database, una variabile statica nella classe Hub o una classe diversa che non deriva da Hub. Se si salvano in modo permanente i dati in memoria, usando un metodo come una variabile statica nella classe Hub, i dati andranno persi quando il dominio dell'app viene riciclato.

Se si vogliono inviare messaggi ai client dal codice che viene eseguito all'esterno della classe Hub, non è possibile crearne un'istanza creando un'istanza della classe Hub, ma è possibile farlo ottenendo un riferimento all'oggetto contesto SignalR per la classe Hub. Per altre informazioni, vedere Come chiamare i metodi client e gestire i gruppi dall'esterno della classe Hub più avanti in questo argomento.

Maiuscole e minuscole dei nomi hub nei client JavaScript

Per impostazione predefinita, i client JavaScript fanno riferimento a Hub usando una versione camel del nome della classe. SignalR apporta automaticamente questa modifica in modo che il codice JavaScript possa essere conforme alle convenzioni JavaScript. L'esempio precedente viene definito come contosoChatHub nel codice JavaScript.

Server

public class ContosoChatHub : Hub

Client JavaScript con proxy generato

var contosoChatHubProxy = $.connection.contosoChatHub;

Se si vuole specificare un nome diverso per i client da usare, aggiungere l'attributo HubName . Quando si usa un HubName attributo, non viene apportata alcuna modifica del nome al case camel nei client JavaScript.

Server

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

Client JavaScript con proxy generato

var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;

Più hub

È possibile definire più classi hub in un'applicazione. Quando si esegue questa operazione, la connessione viene condivisa, ma i gruppi sono separati:

  • Tutti i client useranno lo stesso URL per stabilire una connessione SignalR con il servizio ("/signalr" o l'URL personalizzato, se specificato) e tale connessione viene usata per tutti gli hub definiti dal servizio.

    Non esiste alcuna differenza di prestazioni per più hub rispetto alla definizione di tutte le funzionalità dell'hub in una singola classe.

  • Tutti gli hub ottengono le stesse informazioni di richiesta HTTP.

    Poiché tutti gli hub condividono la stessa connessione, le uniche informazioni sulla richiesta HTTP che il server ottiene è ciò che viene fornito nella richiesta HTTP originale che stabilisce la connessione SignalR. Se si usa la richiesta di connessione per passare informazioni dal client al server specificando una stringa di query, non è possibile fornire stringhe di query diverse a hub diversi. Tutti gli hub riceveranno le stesse informazioni.

  • Il file proxy JavaScript generato conterrà proxy per tutti gli hub in un unico file.

    Per informazioni sui proxy JavaScript, vedere Guida all'API di SignalR Hubs - Client JavaScript - Proxy generato e operazioni da eseguire automaticamente.

  • I gruppi vengono definiti all'interno di Hub.

    In SignalR è possibile definire gruppi denominati da trasmettere a subset di client connessi. I gruppi vengono mantenuti separatamente per ogni hub. Ad esempio, un gruppo denominato "Administrators" include un set di client per la ContosoChatHub classe e lo stesso nome di gruppo fa riferimento a un set diverso di client per la StockTickerHub classe.

Come definire i metodi nella classe Hub che i client possono chiamare

Per esporre un metodo nell'hub che si vuole chiamare dal client, dichiarare un metodo pubblico, come illustrato negli esempi seguenti.

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();
    }
}

È possibile specificare un tipo restituito e parametri, inclusi tipi complessi e matrici, come in qualsiasi metodo C#. Tutti i dati ricevuti nei parametri o restituiti al chiamante vengono comunicati tra il client e il server tramite JSON e SignalR gestisce automaticamente l'associazione di oggetti complessi e matrici di oggetti.

Maiuscole e minuscole dei nomi dei metodi nei client JavaScript

Per impostazione predefinita, i client JavaScript fanno riferimento ai metodi hub usando una versione con maiuscole e minuscole camel del nome del metodo. SignalR apporta automaticamente questa modifica in modo che il codice JavaScript possa essere conforme alle convenzioni JavaScript.

Server

public void NewContosoChatMessage(string userName, string message)

Client JavaScript con proxy generato

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

Se si vuole specificare un nome diverso per i client da usare, aggiungere l'attributo HubMethodName .

Server

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

Client JavaScript con proxy generato

contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

Quando eseguire in modo asincrono

Se il metodo sarà a esecuzione prolungata o deve eseguire operazioni che comportano l'attesa, ad esempio una ricerca di database o una chiamata al servizio Web, rendere il metodo Hub asincrono restituendo un oggetto Task (al posto di void return) o Task<T> (al posto del T tipo restituito). Quando si restituisce un Task oggetto dal metodo, SignalR attende il completamento dell'oggetto Task e quindi invia di nuovo il risultato non compresso al client, quindi non esiste alcuna differenza nel modo in cui si codifica la chiamata al metodo nel client.

La creazione di un metodo Hub consente di evitare di bloccare la connessione quando usa il trasporto WebSocket. Quando un metodo Hub viene eseguito in modo sincrono e il trasporto è WebSocket, le chiamate successive dei metodi nell'hub dallo stesso client vengono bloccate fino al completamento del metodo Hub.

L'esempio seguente mostra lo stesso metodo codificato per l'esecuzione in modo sincrono o asincrono, seguito dal codice client JavaScript che funziona per chiamare una delle due versioni.

Sincrono

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

Asincrono - 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>>());
    }
}

Client JavaScript con proxy generato

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

Per altre informazioni su come usare metodi asincroni in ASP.NET 4.5, vedere Uso di metodi asincroni in ASP.NET MVC 4.

Definizione di overload

Se si desidera definire gli overload per un metodo, il numero di parametri in ogni overload deve essere diverso. Se si differenzia un overload specificando solo tipi di parametro diversi, la classe Hub verrà compilata, ma il servizio SignalR genererà un'eccezione in fase di esecuzione quando i client tentano di chiamare uno degli overload.

Come chiamare i metodi client dalla classe Hub

Per chiamare i metodi client dal server, usare la Clients proprietà in un metodo nella classe Hub. L'esempio seguente mostra il codice server che chiama addNewMessageToPage su tutti i client connessi e il codice client che definisce il metodo in un client JavaScript.

Server

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

Client JavaScript con proxy generato

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

Non è possibile ottenere un valore restituito da un metodo client; sintassi, ad int x = Clients.All.add(1,1) esempio, non funziona.

È possibile specificare tipi e matrici complessi per i parametri. Nell'esempio seguente viene passato un tipo complesso al client in un parametro del metodo.

Codice server che chiama un metodo client usando un oggetto complesso

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

Codice server che definisce l'oggetto complesso

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

Client JavaScript con proxy generato

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

Selezione dei client che riceveranno rpc

La proprietà Clients restituisce un oggetto HubConnectionContext che fornisce diverse opzioni per specificare quali client riceveranno rpc:

  • Tutti i client connessi.

    Clients.All.addContosoChatMessageToPage(name, message);
    
  • Solo il client chiamante.

    Clients.Caller.addContosoChatMessageToPage(name, message);
    
  • Tutti i client ad eccezione del client chiamante.

    Clients.Others.addContosoChatMessageToPage(name, message);
    
  • Un client specifico identificato dall'ID connessione.

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

    Questo esempio chiama addContosoChatMessageToPage il client chiamante e ha lo stesso effetto dell'uso Clients.Callerdi .

  • Tutti i client connessi, ad eccezione dei client specificati, identificati dall'ID connessione.

    Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Tutti i client connessi in un gruppo specificato.

    Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • Tutti i client connessi in un gruppo specificato, ad eccezione dei client specificati, identificati dall'ID connessione.

    Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Tutti i client connessi in un gruppo specificato, ad eccezione del client chiamante.

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

Nessuna convalida in fase di compilazione per i nomi dei metodi

Il nome del metodo specificato viene interpretato come oggetto dinamico, il che significa che non è presente alcuna convalida in fase di compilazione o in fase di compilazione. L'espressione viene valutata in fase di esecuzione. Quando viene eseguita la chiamata al metodo, SignalR invia il nome del metodo e i valori dei parametri al client e, se il client ha un metodo che corrisponde al nome, tale metodo viene chiamato e i valori dei parametri vengono passati. Se nel client non viene trovato alcun metodo corrispondente, non viene generato alcun errore. Per informazioni sul formato dei dati trasmessi da SignalR al client in background quando si chiama un metodo client, vedere Introduzione a SignalR.

Corrispondenza del nome del metodo senza distinzione tra maiuscole e minuscole

La corrispondenza dei nomi dei metodi non fa distinzione tra maiuscole e minuscole. Ad esempio, Clients.All.addContosoChatMessageToPage nel server verrà eseguito AddContosoChatMessageToPage, addcontosochatmessagetopageo addContosoChatMessageToPage nel client.

Esecuzione asincrona

Il metodo chiamato viene eseguito in modo asincrono. Qualsiasi codice riportato dopo una chiamata al metodo a un client verrà eseguito immediatamente senza attendere che SignalR finisca di trasmettere i dati ai client, a meno che non si specifichi che le righe di codice successive devono attendere il completamento del metodo. Gli esempi di codice seguenti illustrano come eseguire due metodi client in sequenza, uno usando il codice che funziona in .NET 4.5 e uno usando il codice che funziona in .NET 4.

Esempio di .NET 4.5

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

Esempio di .NET 4

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

Se si usa await o ContinueWith per attendere il completamento di un metodo client prima dell'esecuzione della riga di codice successiva, ciò non significa che i client riceveranno effettivamente il messaggio prima dell'esecuzione della riga di codice successiva. "Completamento" di una chiamata al metodo client significa solo che SignalR ha eseguito tutto il necessario per inviare il messaggio. Se è necessaria la verifica che i client abbiano ricevuto il messaggio, è necessario programmare manualmente tale meccanismo. Ad esempio, è possibile scrivere il codice di un MessageReceived metodo nell'hub e nel addContosoChatMessageToPage metodo nel client che è possibile chiamare MessageReceived dopo aver eseguito qualsiasi operazione da eseguire nel client. Nell'hub MessageReceived è possibile eseguire qualsiasi operazione dipende dalla ricezione effettiva del client e dall'elaborazione della chiamata al metodo originale.

Come usare una variabile stringa come nome del metodo

Per richiamare un metodo client usando una variabile stringa come nome del metodo, eseguire il cast Clients.All (o Clients.Others, Clients.Callere così via) a IClientProxy e quindi chiamare Invoke(methodName, args...).

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

Come gestire l'appartenenza ai gruppi dalla classe Hub

I gruppi in SignalR forniscono un metodo per la trasmissione di messaggi ai subset specificati di client connessi. Un gruppo può avere un numero qualsiasi di client e un client può essere membro di un numero qualsiasi di gruppi.

Per gestire l'appartenenza ai gruppi, usare i metodi Add e Remove forniti dalla Groups proprietà della classe Hub. L'esempio seguente illustra i Groups.Add metodi e Groups.Remove usati nei metodi Hub chiamati dal codice client, seguiti dal codice client JavaScript che li chiama.

Server

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);
    }
}

Client JavaScript con proxy generato

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

Non è necessario creare in modo esplicito i gruppi. In effetti, un gruppo viene creato automaticamente la prima volta che si specifica il nome in una chiamata a Groups.Adde viene eliminato quando si rimuove l'ultima connessione dall'appartenenza.

Non esiste alcuna API per ottenere un elenco di appartenenze a gruppi o un elenco di gruppi. SignalR invia messaggi a client e gruppi basati su un modello pub/sub e il server non gestisce elenchi di gruppi o appartenenze a gruppi. Ciò consente di ottimizzare la scalabilità, perché ogni volta che si aggiunge un nodo a una Web farm, qualsiasi stato gestito da SignalR deve essere propagato al nuovo nodo.

Esecuzione asincrona dei metodi Add e Remove

I Groups.Add metodi e Groups.Remove vengono eseguiti in modo asincrono. Se si vuole aggiungere un client a un gruppo e inviare immediatamente un messaggio al client usando il gruppo, è necessario assicurarsi che il Groups.Add metodo venga completato per primo. Gli esempi di codice seguenti illustrano come eseguire questa operazione, una usando il codice che funziona in .NET 4.5 e una usando il codice che funziona in .NET 4

Esempio di .NET 4.5

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

Esempio di .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"));
}

Persistenza dell'appartenenza a gruppi

SignalR tiene traccia delle connessioni, non degli utenti, quindi se si vuole che un utente si trovi nello stesso gruppo ogni volta che l'utente stabilisce una connessione, è necessario chiamare Groups.Add ogni volta che l'utente stabilisce una nuova connessione.

Dopo una perdita temporanea di connettività, a volte SignalR può ripristinare automaticamente la connessione. In tal caso, SignalR ripristina la stessa connessione, non stabilisce una nuova connessione e quindi viene ripristinata automaticamente l'appartenenza al gruppo del client. Ciò è possibile anche quando l'interruzione temporanea è il risultato di un riavvio o di un errore del server, perché lo stato della connessione per ogni client, incluse le appartenenze ai gruppi, viene eseguito il round-tripped al client. Se un server si arresta e viene sostituito da un nuovo server prima del timeout della connessione, un client può riconnettersi automaticamente al nuovo server e registrarlo nuovamente nei gruppi di cui è membro.

Quando una connessione non può essere ripristinata automaticamente dopo una perdita di connettività o quando si verifica il timeout della connessione o quando il client si disconnette (ad esempio, quando un browser passa a una nuova pagina), le appartenenze ai gruppi vengono perse. La volta successiva che l'utente si connette sarà una nuova connessione. Per mantenere le appartenenze ai gruppi quando lo stesso utente stabilisce una nuova connessione, l'applicazione deve tenere traccia delle associazioni tra utenti e gruppi e ripristinare le appartenenze ai gruppi ogni volta che un utente stabilisce una nuova connessione.

Per altre informazioni sulle connessioni e le riconnessioni, vedere Come gestire gli eventi di durata della connessione nella classe Hub più avanti in questo argomento.

Gruppi di utenti singoli

Le applicazioni che usano SignalR in genere devono tenere traccia delle associazioni tra utenti e connessioni per sapere quale utente ha inviato un messaggio e quali utenti devono ricevere un messaggio. I gruppi vengono usati in uno dei due modelli comunemente usati per eseguire questa operazione.

  • Gruppi di utenti singoli.

    È possibile specificare il nome utente come nome del gruppo e aggiungere l'ID connessione corrente al gruppo ogni volta che l'utente si connette o si riconnette. Per inviare messaggi all'utente inviato al gruppo. Uno svantaggio di questo metodo è che il gruppo non fornisce un modo per scoprire se l'utente è online o offline.

  • Tenere traccia delle associazioni tra i nomi utente e gli ID di connessione.

    È possibile archiviare un'associazione tra ogni nome utente e uno o più ID di connessione in un dizionario o un database e aggiornare i dati archiviati ogni volta che l'utente si connette o si disconnette. Per inviare messaggi all'utente, specificare gli ID di connessione. Uno svantaggio di questo metodo è che richiede più memoria.

Come gestire gli eventi di durata della connessione nella classe Hub

I motivi tipici per la gestione degli eventi di durata della connessione sono tenere traccia del fatto che un utente sia connesso o meno e tenere traccia dell'associazione tra i nomi utente e gli ID connessione. Per eseguire codice personalizzato quando i client si connettono o si disconnettono, eseguire l'override dei OnConnectedmetodi virtuali , OnDisconnectede OnReconnected della classe Hub, come illustrato nell'esempio seguente.

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 vengono chiamati

Ogni volta che un browser passa a una nuova pagina, deve essere stabilita una nuova connessione, il che significa che SignalR eseguirà il OnDisconnected metodo seguito dal OnConnected metodo . SignalR crea sempre un nuovo ID di connessione quando viene stabilita una nuova connessione.

Il OnReconnected metodo viene chiamato quando si è verificata un'interruzione temporanea della connettività da cui SignalR può eseguire automaticamente il ripristino, ad esempio quando un cavo viene temporaneamente disconnesso e riconnesso prima del timeout della connessione. Il OnDisconnected metodo viene chiamato quando il client viene disconnesso e SignalR non può riconnettersi automaticamente, ad esempio quando un browser passa a una nuova pagina. Pertanto, una possibile sequenza di eventi per un determinato client è OnConnected, , OnReconnectedOnDisconnected; o . OnDisconnectedOnConnected La sequenza OnConnected, OnDisconnected, OnReconnected non verrà visualizzata per una determinata connessione.

Il OnDisconnected metodo non viene chiamato in alcuni scenari, ad esempio quando un server si arresta o il dominio app viene riciclato. Quando un altro server viene attivato in linea o il dominio app completa il riciclo, alcuni client potrebbero essere in grado di riconnettersi e generare l'evento OnReconnected .

Per altre informazioni, vedere Understanding and Handling Connection Lifetime Events in SignalR.For more information, see Understanding and Handling Connection Lifetime Events in SignalR.

Stato del chiamante non popolato

I metodi del gestore eventi della durata della connessione vengono chiamati dal server, il che significa che qualsiasi stato inserito nell'oggetto state nel client non verrà popolato nella Caller proprietà nel server. Per informazioni sull'oggetto state e sulla Caller proprietà, vedere Come passare lo stato tra i client e la classe Hub più avanti in questo argomento.

Come ottenere informazioni sul client dalla proprietà Context

Per ottenere informazioni sul client, usare la Context proprietà della classe Hub. La Context proprietà restituisce un oggetto HubCallerContext che fornisce l'accesso alle informazioni seguenti:

  • ID connessione del client chiamante.

    string connectionID = Context.ConnectionId;
    

    L'ID connessione è un GUID assegnato da SignalR (non è possibile specificare il valore nel proprio codice). Esiste un ID di connessione per ogni connessione e lo stesso ID di connessione viene usato da tutti gli hub se nell'applicazione sono presenti più hub.

  • Dati di intestazione HTTP.

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

    È anche possibile ottenere intestazioni HTTP da Context.Headers. Il motivo di più riferimenti alla stessa cosa è che Context.Headers è stato creato per primo, la Context.Request proprietà è stata aggiunta in un secondo momento ed Context.Headers è stata mantenuta per la compatibilità con le versioni precedenti.

  • Eseguire query sui dati della stringa.

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

    È anche possibile ottenere dati di stringa di query da Context.QueryString.

    La stringa di query che si ottiene in questa proprietà è quella usata con la richiesta HTTP che ha stabilito la connessione SignalR. È possibile aggiungere parametri di stringa di query nel client configurando la connessione, che è un modo pratico per passare i dati sul client dal client al server. Nell'esempio seguente viene illustrato un modo per aggiungere una stringa di query in un client JavaScript quando si usa il proxy generato.

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

    Per altre informazioni sull'impostazione dei parametri della stringa di query, vedere le guide API per i client JavaScript e .NET .

    È possibile trovare il metodo di trasporto usato per la connessione nei dati della stringa di query, insieme ad altri valori usati internamente da SignalR:

    string transportMethod = queryString["transport"];
    

    Il valore di transportMethod sarà "webSockets", "serverSentEvents", "foreverFrame" o "longPolling". Si noti che se si controlla questo valore nel metodo del OnConnected gestore eventi, in alcuni scenari è possibile ottenere inizialmente un valore di trasporto che non è il metodo di trasporto negoziato finale per la connessione. In tal caso, il metodo genererà un'eccezione e verrà chiamato di nuovo in un secondo momento quando viene stabilito il metodo di trasporto finale.

  • Biscotti.

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

    È anche possibile ottenere i cookie da Context.RequestCookies.

  • informazioni utente.

    System.Security.Principal.IPrincipal user = Context.User;
    
  • Oggetto HttpContext per la richiesta :

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

    Usare questo metodo invece di ottenere HttpContext.Current l'oggetto HttpContext per la connessione SignalR.

Come passare lo stato tra i client e la classe Hub

Il proxy client fornisce un state oggetto in cui è possibile archiviare i dati da trasmettere al server con ogni chiamata al metodo. Nel server è possibile accedere a questi dati nella Clients.Caller proprietà nei metodi hub chiamati dai client. La Clients.Caller proprietà non viene popolata per i metodi OnConnecteddel gestore eventi della durata della connessione , OnDisconnectede OnReconnected.

La creazione o l'aggiornamento dei dati nell'oggetto state e la Clients.Caller proprietà funziona in entrambe le direzioni. È possibile aggiornare i valori nel server e vengono passati di nuovo al client.

L'esempio seguente mostra il codice client JavaScript che archivia lo stato per la trasmissione al server con ogni chiamata al metodo.

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

L'esempio seguente illustra il codice equivalente in un client .NET.

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

Nella classe Hub è possibile accedere a questi dati nella Clients.Caller proprietà . Nell'esempio seguente viene illustrato il codice che recupera lo stato a cui fa riferimento nell'esempio precedente.

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

Nota

Questo meccanismo per la persistenza dello stato non è destinato a grandi quantità di dati, poiché tutti gli elementi inseriti nella state proprietà o Clients.Caller vengono arrotondati con ogni chiamata al metodo. È utile per elementi più piccoli, ad esempio nomi utente o contatori.

Come gestire gli errori nella classe Hub

Per gestire gli errori che si verificano nei metodi della classe Hub, usare uno o entrambi i metodi seguenti:

  • Eseguire il wrapping del codice del metodo in blocchi try-catch e registrare l'oggetto eccezione. Ai fini del debug è possibile inviare l'eccezione al client, ma per motivi di sicurezza non è consigliabile inviare informazioni dettagliate ai client nell'ambiente di produzione.

  • Creare un modulo della pipeline hubs che gestisce il metodo OnIncomingError . L'esempio seguente illustra un modulo della pipeline che registra gli errori, seguito dal codice in Global.asax che inserisce il modulo nella pipeline di 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();
    }
    

Per altre informazioni sui moduli della pipeline hub, vedere Come personalizzare la pipeline hub più avanti in questo argomento.

Come abilitare la traccia

Per abilitare la traccia lato server, aggiungere un elemento system.diagnostics al file Web.config, come illustrato in questo esempio:

<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>

Quando si esegue l'applicazione in Visual Studio, è possibile visualizzare i log nella finestra Output .

Come chiamare i metodi client e gestire i gruppi dall'esterno della classe Hub

Per chiamare i metodi client da una classe diversa dalla classe Hub, ottenere un riferimento all'oggetto contesto SignalR per l'hub e usarlo per chiamare i metodi nel client o gestire i gruppi.

La classe di esempio StockTicker seguente ottiene l'oggetto contesto, la archivia in un'istanza della classe , archivia l'istanza della classe in una proprietà statica e usa il contesto dell'istanza della classe singleton per chiamare il updateStockPrice metodo sui client connessi a un hub denominato 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 è necessario usare il contesto più volte in un oggetto di lunga durata, ottenere il riferimento una sola volta e salvarlo invece di recuperarlo di nuovo ogni volta. Ottenere il contesto una volta garantisce che SignalR invii messaggi ai client nella stessa sequenza in cui i metodi hub effettuano chiamate al metodo client. Per un'esercitazione che illustra come usare il contesto SignalR per un hub, vedere Trasmissione del server con ASP.NET SignalR.

Chiamata dei metodi client

È possibile specificare quali client riceveranno RPC, ma sono disponibili meno opzioni rispetto a quando si chiama da una classe Hub. Il motivo è che il contesto non è associato a una chiamata specifica da un client, pertanto tutti i metodi che richiedono conoscenza dell'ID connessione corrente, ad esempio Clients.Others, o Clients.Callero Clients.OthersInGroup, non sono disponibili. Sono disponibili le opzioni seguenti:

  • Tutti i client connessi.

    context.Clients.All.addContosoChatMessageToPage(name, message);
    
  • Un client specifico identificato dall'ID connessione.

    context.Clients.Client(connectionID).addContosoChatMessageToPage(name, message);
    
  • Tutti i client connessi, ad eccezione dei client specificati, identificati dall'ID connessione.

    context.Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Tutti i client connessi in un gruppo specificato.

    context.Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • Tutti i client connessi in un gruppo specificato, ad eccezione dei client specificati, identificati dall'ID connessione.

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

Se si sta chiamando nella classe non hub dai metodi nella classe Hub, è possibile passare l'ID connessione corrente e usarlo con Clients.Client, o per simulare Clients.Caller, Clients.Otherso Clients.GroupClients.OthersInGroup. Clients.AllExcept Nell'esempio seguente la MoveShapeHub classe passa l'ID connessione alla Broadcaster classe in modo che la Broadcaster classe possa simulare 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;
        }
    }
}

Gestione dell'appartenenza a gruppi

Per la gestione dei gruppi sono disponibili le stesse opzioni eseguite in una classe Hub.

  • Aggiungere un client a un gruppo

    context.Groups.Add(connectionID, groupName);
    
  • Rimuovere un client da un gruppo

    context.Groups.Remove(connectionID, groupName);
    

Come personalizzare la pipeline hub

SignalR consente di inserire il proprio codice nella pipeline dell'hub. Nell'esempio seguente viene illustrato un modulo della pipeline hub personalizzato che registra ogni chiamata al metodo in ingresso ricevuta dal client e dalla chiamata al metodo in uscita richiamata nel client:

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); 
    } 
}

Il codice seguente nel file Global.asax registra il modulo da eseguire nella pipeline hub:

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

Esistono molti metodi diversi che è possibile eseguire l'override. Per un elenco completo, vedere Metodi HubPipelineModule.