przewodnik interfejsu API usługi SignalR Hubs ASP.NET — serwer (SignalR 1.x)

Autor: Patrick Fletcher, Tom Dykstra

Ostrzeżenie

Ta dokumentacja nie dotyczy najnowszej wersji usługi SignalR. Przyjrzyj się ASP.NET Core SignalR.

Ten dokument zawiera wprowadzenie do programowania po stronie serwera interfejsu API usługi ASP.NET SignalR Hubs dla usługi SignalR w wersji 1.1 z przykładami kodu demonstrującymi typowe opcje.

Interfejs API usługi SignalR Hubs umożliwia wykonywanie zdalnych wywołań procedur (RPC) z serwera do połączonych klientów i od klientów do serwera. W kodzie serwera definiuje się metody, które mogą być wywoływane przez klientów, a metody uruchamiane na kliencie. W kodzie klienta definiuje się metody, które mogą być wywoływane z serwera, a metody uruchamiane na serwerze. Usługa SignalR zajmuje się całą instalacją wodną typu klient-serwer.

Usługa SignalR oferuje również interfejs API niższego poziomu o nazwie Połączenia trwałe. Aby zapoznać się z wprowadzeniem do usługi SignalR, centrów i połączeń trwałych lub samouczka przedstawiającego sposób kompilowania kompletnej aplikacji SignalR, zobacz SignalR — Wprowadzenie.

Omówienie

Ten dokument zawiera następujące sekcje:

Aby uzyskać dokumentację dotyczącą programowania klientów, zobacz następujące zasoby:

Linki do tematów referencyjnych interfejsu API to wersja interfejsu API platformy .NET 4.5. Jeśli używasz platformy .NET 4, zobacz tematy dotyczące platformy .NET 4 interfejsu API.

Jak zarejestrować trasę usługi SignalR i skonfigurować opcje usługi SignalR

Aby zdefiniować trasę używaną przez klientów do nawiązywania połączenia z centrum, wywołaj metodę MapHubs po uruchomieniu aplikacji. MapHubs jest metodą rozszerzenia dla System.Web.Routing.RouteCollection klasy . W poniższym przykładzie pokazano, jak zdefiniować trasę usługi SignalR Hubs w pliku Global.asax .

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

Jeśli dodasz funkcję SignalR do aplikacji MVC ASP.NET, upewnij się, że trasa usługi SignalR jest dodawana przed innymi trasami. Aby uzyskać więcej informacji, zobacz Tutorial: Wprowadzenie with SignalR and MVC 4 (Samouczek: Wprowadzenie z usługami SignalR i MVC 4).

Adres URL /signalr

Domyślnie adres URL trasy używany przez klientów do nawiązywania połączenia z koncentratorem to "/signalr". (Nie należy mylić tego adresu URL z adresem URL "/signalr/hubs", który jest przeznaczony dla automatycznie wygenerowanego pliku JavaScript. Aby uzyskać więcej informacji na temat wygenerowanego serwera proxy, zobacz SignalR Hubs API Guide - JavaScript Client — The generated proxy and what it does for you (Przewodnik po interfejsie API usługi SignalR Hubs — klient JavaScript — wygenerowany serwer proxy i co robi).

Mogą wystąpić nadzwyczajne okoliczności, które sprawiają, że ten podstawowy adres URL nie może być używany dla usługi SignalR; na przykład masz folder w projekcie o nazwie signalr i nie chcesz zmieniać nazwy. W takim przypadku możesz zmienić podstawowy adres URL, jak pokazano w poniższych przykładach (zastąp ciąg "/signalr" w przykładowym kodzie żądanym adresem URL).

Kod serwera określający adres URL

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

Kod klienta JavaScript określający adres URL (z wygenerowany serwer proxy)

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

Kod klienta JavaScript określający adres URL (bez wygenerowanego serwera proxy)

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

Kod klienta platformy .NET określający adres URL

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

Konfigurowanie opcji signalr

Przeciążenia MapHubs metody umożliwiają określenie niestandardowego adresu URL, niestandardowego programu rozpoznawania zależności i następujących opcji:

W poniższym przykładzie pokazano, jak określić adres URL połączenia usługi SignalR i te opcje w wywołaniu MapHubs metody . Aby określić niestandardowy adres URL, zastąp ciąg "/signalr" w przykładzie adresem URL, którego chcesz użyć.

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

Jak tworzyć klasy centrum i korzystać z nich

Aby utworzyć centrum, utwórz klasę pochodną Microsoft.Aspnet.Signalr.Hub. W poniższym przykładzie przedstawiono prostą klasę Centrum dla aplikacji do czatu.

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

W tym przykładzie połączony klient może wywołać metodę NewContosoChatMessage , a gdy tak, dane odebrane są rozgłaszane do wszystkich połączonych klientów.

Okres istnienia obiektu koncentratora

Nie tworzysz wystąpienia klasy Hub ani nie wywołujesz jej metod z własnego kodu na serwerze; Wszystko to jest wykonywane przez potok usługi SignalR Hubs. Usługa SignalR tworzy nowe wystąpienie klasy Centrum za każdym razem, gdy musi obsługiwać operację centrum, taką jak gdy klient łączy się, rozłącza lub wykonuje wywołanie metody do serwera.

Ponieważ wystąpienia klasy Hub są przejściowe, nie można ich używać do zachowania stanu z jednego wywołania metody do następnego. Za każdym razem, gdy serwer odbiera wywołanie metody od klienta, nowe wystąpienie klasy Hub przetwarza komunikat. Aby zachować stan za pośrednictwem wielu połączeń i wywołań metod, użyj innej metody, takiej jak baza danych, lub zmiennej statycznej w klasie Hub lub innej klasy, która nie pochodzi z klasy Hub. W przypadku utrwalania danych w pamięci przy użyciu metody takiej jak zmienna statyczna w klasie Hub dane zostaną utracone po odtworzeniu domeny aplikacji.

Jeśli chcesz wysyłać komunikaty do klientów z własnego kodu, który działa poza klasą Hub, nie można tego zrobić, tworząc wystąpienie klasy Hub, ale możesz to zrobić, uzyskując odwołanie do obiektu kontekstu usługi SignalR dla klasy Centrum. Aby uzyskać więcej informacji, zobacz Jak wywoływać metody klienta i zarządzać grupami spoza klasy Hub w dalszej części tego tematu.

Wielbłądowe nazwy centrów w klientach Języka JavaScript

Domyślnie klienci języka JavaScript odwołują się do usługi Hubs przy użyciu wielbłądowej wersji nazwy klasy. Usługa SignalR automatycznie wprowadza tę zmianę, aby kod JavaScript mógł być zgodny z konwencjami języka JavaScript. Poprzedni przykład będzie określany jako contosoChatHub w kodzie JavaScript.

Server (Serwer)

public class ContosoChatHub : Hub

Klient JavaScript korzystający z wygenerowanego serwera proxy

var contosoChatHubProxy = $.connection.contosoChatHub;

Jeśli chcesz określić inną nazwę dla klientów do użycia, dodaj HubName atrybut. Jeśli używasz atrybutu HubName , nie ma zmiany nazwy na przypadek camel na klientach JavaScript.

Server (Serwer)

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

Klient JavaScript korzystający z wygenerowanego serwera proxy

var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;

Wiele koncentratorów

W aplikacji można zdefiniować wiele klas centrum. W takim przypadku połączenie jest współużytkowane, ale grupy są oddzielne:

  • Wszyscy klienci będą używać tego samego adresu URL, aby nawiązać połączenie usługi SignalR z usługą ("/signalr" lub niestandardowy adres URL, jeśli został określony), a to połączenie jest używane dla wszystkich centrów zdefiniowanych przez usługę.

    Nie ma różnicy w wydajności dla wielu centrów w porównaniu do definiowania wszystkich funkcji centrum w jednej klasie.

  • Wszystkie centra uzyskują te same informacje o żądaniu HTTP.

    Ponieważ wszystkie koncentratory współdzielą to samo połączenie, jedynymi informacjami o żądaniu HTTP, które otrzymuje serwer, jest to, co jest dostępne w oryginalnym żądaniu HTTP, które ustanawia połączenie usługi SignalR. Jeśli używasz żądania połączenia, aby przekazać informacje od klienta do serwera, określając parametry zapytania, nie można podać różnych parametrów zapytania do różnych centrów. Wszystkie centra otrzymają te same informacje.

  • Wygenerowany plik serwerów proxy języka JavaScript będzie zawierać serwery proxy dla wszystkich centrów w jednym pliku.

    Aby uzyskać informacje na temat serwerów proxy języka JavaScript, zobacz SignalR Hubs API Guide - JavaScript Client — wygenerowany serwer proxy i co to robi.

  • Grupy są definiowane w usłudze Hubs.

    W usłudze SignalR można zdefiniować nazwane grupy do emisji do podzestawów połączonych klientów. Grupy są przechowywane oddzielnie dla każdego centrum. Na przykład grupa o nazwie "Administratorzy" będzie zawierać jeden zestaw klientów dla ContosoChatHub klasy, a ta sama nazwa grupy odwołuje się do innego zestawu klientów dla klasy StockTickerHub .

Jak zdefiniować metody w klasie Hub, którą klienci mogą wywoływać

Aby uwidocznić metodę w centrum, którą chcesz wywołać z klienta, zadeklaruj metodę publiczną, jak pokazano w poniższych przykładach.

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

Można określić typ i parametry zwracane, w tym typy złożone i tablice, tak jak w dowolnej metodzie języka C#. Wszystkie dane odbierane w parametrach lub zwracane do obiektu wywołującego są przekazywane między klientem a serwerem przy użyciu formatu JSON, a usługa SignalR automatycznie obsługuje powiązanie złożonych obiektów i tablic obiektów.

Wielbłądowe nazwy metod w klientach JavaScript

Domyślnie klienci języka JavaScript odwołują się do metod centrum przy użyciu wielbłądowej wersji nazwy metody. Usługa SignalR automatycznie wprowadza tę zmianę, aby kod JavaScript mógł być zgodny z konwencjami języka JavaScript.

Server (Serwer)

public void NewContosoChatMessage(string userName, string message)

Klient JavaScript korzystający z wygenerowanego serwera proxy

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

Jeśli chcesz określić inną nazwę dla klientów do użycia, dodaj HubMethodName atrybut.

Server (Serwer)

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

Klient JavaScript korzystający z wygenerowanego serwera proxy

contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

Kiedy należy wykonać asynchronicznie

Jeśli metoda będzie długotrwała lub musi wykonać pracę, która będzie obejmować oczekiwanie, takie jak wyszukiwanie bazy danych lub wywołanie usługi internetowej, wykonaj asynchroniczne metodę Hub, zwracając zadanie (zamiast void zwracania) lub obiekt T> zadania< (zamiast typu zwracanegoT). Gdy zwracasz Task obiekt z metody, usługa SignalR czeka na Task zakończenie, a następnie wysyła niezapisany wynik z powrotem do klienta, więc nie ma różnicy w kodzie wywołania metody w kliencie.

Tworzenie metody asynchronicznej centrum pozwala uniknąć blokowania połączenia w przypadku korzystania z transportu protokołu WebSocket. Gdy metoda centrum jest wykonywana synchronicznie, a transport to WebSocket, kolejne wywołania metod w centrum z tego samego klienta zostaną zablokowane do momentu zakończenia metody Hub.

W poniższym przykładzie pokazano tę samą metodę kodowaną do uruchamiania synchronicznie lub asynchronicznie, a następnie kod klienta JavaScript, który działa do wywoływania jednej z wersji.

Synchronous

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

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

Klient JavaScript korzystający z wygenerowanego serwera proxy

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

Aby uzyskać więcej informacji na temat używania metod asynchronicznych w ASP.NET 4.5, zobacz Using Asynchronous Methods in ASP.NET MVC 4 (Używanie metod asynchronicznych w ASP.NET MVC 4).

Definiowanie przeciążeń

Jeśli chcesz zdefiniować przeciążenia dla metody, liczba parametrów w każdym przeciążeniu musi być inna. Jeśli rozróżniasz przeciążenie tylko przez określenie różnych typów parametrów, klasa centrum skompiluje, ale usługa SignalR zgłosi wyjątek w czasie wykonywania, gdy klienci próbują wywołać jedno z przeciążeń.

Jak wywoływać metody klienta z klasy Hub

Aby wywołać metody klienta z serwera, użyj Clients właściwości w metodzie w klasie Hub. W poniższym przykładzie pokazano kod serwera, który wywołuje addNewMessageToPage wszystkich połączonych klientów, oraz kod klienta definiujący metodę w kliencie JavaScript.

Server (Serwer)

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

Klient JavaScript korzystający z wygenerowanego serwera proxy

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

Nie można uzyskać wartości zwracanej z metody klienta; składnia, taka jak int x = Clients.All.add(1,1) nie działa.

Dla parametrów można określić złożone typy i tablice. Poniższy przykład przekazuje do klienta typ złożony w parametrze metody.

Kod serwera, który wywołuje metodę klienta przy użyciu złożonego obiektu

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

Kod serwera definiujący złożony obiekt

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

Klient JavaScript korzystający z wygenerowanego serwera proxy

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

Wybieranie klientów, którzy otrzymają RPC

Właściwość Clients zwraca obiekt HubConnectionContext , który udostępnia kilka opcji określania, którzy klienci otrzymają RPC:

  • Wszyscy połączeni klienci.

    Clients.All.addContosoChatMessageToPage(name, message);
    
  • Tylko wywołujący klient.

    Clients.Caller.addContosoChatMessageToPage(name, message);
    
  • Wszyscy klienci z wyjątkiem klienta wywołującego.

    Clients.Others.addContosoChatMessageToPage(name, message);
    
  • Określony klient zidentyfikowany przez identyfikator połączenia.

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

    W tym przykładzie wywołuje addContosoChatMessageToPage klienta wywołującego i ma taki sam wpływ jak w przypadku używania polecenia Clients.Caller.

  • Wszyscy połączeni klienci z wyjątkiem określonych klientów zidentyfikowanych przez identyfikator połączenia.

    Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Wszyscy połączeni klienci w określonej grupie.

    Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • Wszyscy połączeni klienci w określonej grupie z wyjątkiem określonych klientów zidentyfikowanych przez identyfikator połączenia.

    Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Wszyscy połączeni klienci w określonej grupie z wyjątkiem klienta wywołującego.

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

Brak walidacji czasu kompilacji dla nazw metod

Określona nazwa metody jest interpretowana jako obiekt dynamiczny, co oznacza, że nie ma dla niej funkcji IntelliSense ani walidacji czasu kompilacji. Wyrażenie jest oceniane w czasie wykonywania. Po wykonaniu wywołania metody usługa SignalR wysyła nazwę metody i wartości parametrów do klienta, a jeśli klient ma metodę zgodną z nazwą, wywoływana jest ta metoda, a wartości parametrów są do niego przekazywane. Jeśli na kliencie nie zostanie znaleziona żadna zgodna metoda, nie zostanie zgłoszony żaden błąd. Aby uzyskać informacje o formacie danych przesyłanych przez usługę SignalR do klienta w tle podczas wywoływania metody klienta, zobacz Wprowadzenie do usługi SignalR.

Dopasowywanie nazwy metody bez uwzględniania wielkości liter

Dopasowanie nazwy metody jest bez uwzględniania wielkości liter. Na przykład Clients.All.addContosoChatMessageToPage na serwerze zostanie wykonane AddContosoChatMessageToPagepolecenie , addcontosochatmessagetopagelub addContosoChatMessageToPage na kliencie.

Wykonywanie asynchroniczne

Wywołana metoda wykonuje asynchronicznie. Każdy kod, który następuje po wywołaniu metody do klienta, zostanie wykonany natychmiast bez oczekiwania na zakończenie przesyłania danych do klientów, chyba że kolejne wiersze kodu powinny czekać na ukończenie metody. Poniższe przykłady kodu pokazują, jak wykonać dwie metody klienta sekwencyjnie, jeden przy użyciu kodu, który działa na platformie .NET 4.5, i jeden przy użyciu kodu, który działa na platformie .NET 4.

Przykład platformy .NET 4.5

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

Przykład platformy .NET 4

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

Jeśli użyjesz await metody klienta lub ContinueWith zaczekasz na zakończenie metody klienta przed wykonaniem następnego wiersza kodu, nie oznacza to, że klienci będą rzeczywiście otrzymywać komunikat przed wykonaniem następnego wiersza kodu. "Ukończenie" wywołania metody klienta oznacza tylko, że usługa SignalR wykonała wszystko, co jest niezbędne do wysłania komunikatu. Jeśli potrzebujesz weryfikacji, że klienci otrzymali komunikat, musisz samodzielnie zaprogramować ten mechanizm. Można na przykład kodować metodę MessageReceived w centrum, a w addContosoChatMessageToPage metodzie na kliencie można wywołać MessageReceived po wykonaniu dowolnej pracy, którą musisz wykonać na kliencie. W MessageReceived centrum możesz wykonać niezależnie od rzeczywistego odbioru klienta i przetwarzania oryginalnego wywołania metody.

Jak użyć zmiennej ciągu jako nazwy metody

Jeśli chcesz wywołać metodę klienta przy użyciu zmiennej ciągu jako nazwy metody, rzutowania Clients.All (lub Clients.Others, Clients.Calleritp.), aby i wywołać IClientProxy metodę Invoke(methodName, args...).

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

Jak zarządzać członkostwem w grupie z klasy Hub

Grupy w usłudze SignalR udostępniają metodę nadawania komunikatów określonym podzbiorom połączonych klientów. Grupa może mieć dowolną liczbę klientów, a klient może być członkiem dowolnej liczby grup.

Aby zarządzać członkostwem w grupie, użyj metod Add and Remove udostępnianych przez Groups właściwość klasy Hub. W poniższym przykładzie przedstawiono Groups.Add metody i Groups.Remove używane w metodach centrum, które są wywoływane przez kod klienta, a następnie kod klienta JavaScript, który je wywołuje.

Server (Serwer)

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

Klient JavaScript korzystający z wygenerowanego serwera proxy

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

Nie musisz jawnie tworzyć grup. W efekcie grupa jest tworzona automatycznie przy pierwszym określeniu jej nazwy w wywołaniu metody Groups.Addi jest usuwana po usunięciu ostatniego połączenia z członkostwa w nim.

Brak interfejsu API do pobierania listy członkostwa w grupie ani listy grup. Usługa SignalR wysyła komunikaty do klientów i grup na podstawie modelu pub/podrzędnego, a serwer nie obsługuje list grup ani członkostwa w grupach. Pomaga to zmaksymalizować skalowalność, ponieważ za każdym razem, gdy dodasz węzeł do farmy internetowej, do nowego węzła musi zostać rozpropagowany dowolny stan utrzymywany przez usługę SignalR.

Asynchroniczne wykonywanie metod Add and Remove

Metody Groups.Add i Groups.Remove są wykonywane asynchronicznie. Jeśli chcesz dodać klienta do grupy i natychmiast wysłać komunikat do klienta przy użyciu grupy, musisz upewnić się, że Groups.Add metoda zakończy się najpierw. W poniższych przykładach kodu pokazano, jak to zrobić, korzystając z kodu, który działa na platformie .NET 4.5, a drugi przy użyciu kodu, który działa na platformie .NET 4

Przykład platformy .NET 4.5

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

Przykład platformy .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"));
}

Trwałość członkostwa w grupie

Usługa SignalR śledzi połączenia, a nie użytkowników, więc jeśli chcesz, aby użytkownik był w tej samej grupie za każdym razem, gdy użytkownik nawiązuje połączenie, musisz wywołać Groups.Add je za każdym razem, gdy użytkownik nawiązuje nowe połączenie.

Po tymczasowej utracie łączności usługa SignalR może automatycznie przywrócić połączenie. W takim przypadku usługa SignalR przywraca to samo połączenie, nie ustanawia nowego połączenia, dlatego członkostwo w grupie klienta jest automatycznie przywracane. Jest to możliwe nawet wtedy, gdy tymczasowy przerwa jest wynikiem ponownego uruchomienia serwera lub awarii, ponieważ stan połączenia dla każdego klienta, w tym członkostwa w grupach, jest zaokrąglony do klienta. Jeśli serwer ulegnie awarii i zostanie zastąpiony przez nowy serwer przed upływem limitu czasu połączenia, klient może ponownie nawiązać połączenie z nowym serwerem i ponownie zarejestrować je w grupach, do których należy.

Jeśli nie można przywrócić połączenia automatycznie po utracie łączności lub przekroczeniu limitu czasu połączenia lub gdy klient rozłącza się (na przykład po przejściu przeglądarki do nowej strony), członkostwo w grupach zostanie utracone. Następnym razem, gdy użytkownik połączy się, będzie nowe połączenie. Aby zachować członkostwo w grupach, gdy ten sam użytkownik ustanawia nowe połączenie, aplikacja musi śledzić skojarzenia między użytkownikami i grupami oraz przywracać członkostwo w grupach za każdym razem, gdy użytkownik ustanawia nowe połączenie.

Aby uzyskać więcej informacji na temat połączeń i ponownych połączeń, zobacz Jak obsługiwać zdarzenia okresu istnienia połączenia w klasie Hub w dalszej części tego tematu.

Grupy pojedynczego użytkownika

Aplikacje korzystające z usługi SignalR zwykle muszą śledzić skojarzenia między użytkownikami i połączeniami, aby dowiedzieć się, który użytkownik wysłał komunikat i które użytkowników powinny otrzymywać komunikat. Grupy są używane w jednym z dwóch często używanych wzorców w tym celu.

  • Grupy pojedynczego użytkownika.

    Możesz określić nazwę użytkownika jako nazwę grupy i dodać bieżący identyfikator połączenia do grupy za każdym razem, gdy użytkownik nawiązuje połączenie lub ponownie nawiąż połączenie. Aby wysyłać wiadomości do użytkownika, który wysyłasz do grupy. Wadą tej metody jest to, że grupa nie zapewnia sposobu, aby dowiedzieć się, czy użytkownik jest w trybie online, czy offline.

  • Śledzenie skojarzeń między nazwami użytkowników i identyfikatorami połączeń.

    Skojarzenie między każdą nazwą użytkownika i co najmniej jednym identyfikatorem połączenia można przechowywać w słowniku lub bazie danych, a także aktualizować przechowywane dane za każdym razem, gdy użytkownik łączy się lub rozłącza. Aby wysyłać komunikaty do użytkownika, określ identyfikatory połączeń. Wadą tej metody jest to, że zajmuje więcej pamięci.

Jak obsługiwać zdarzenia okresu istnienia połączenia w klasie Hub

Typowe przyczyny obsługi zdarzeń okresu istnienia połączenia to śledzenie, czy użytkownik jest połączony, czy nie, oraz śledzenie skojarzenia między nazwami użytkowników i identyfikatorami połączeń. Aby uruchomić własny kod, gdy klienci nawiązują połączenie lub rozłączą, przesłoń OnConnectedmetody , OnDisconnectedi OnReconnected klasy Hub, jak pokazano w poniższym przykładzie.

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

Gdy są wywoływane połączenia OnConnected, OnDisconnected i OnReconnected

Za każdym razem, gdy przeglądarka przechodzi do nowej strony, należy ustanowić nowe połączenie, co oznacza, że usługa SignalR wykona OnDisconnected metodę, po której następuje OnConnected metoda . Usługa SignalR zawsze tworzy nowy identyfikator połączenia po nawiązaniu nowego połączenia.

Metoda OnReconnected jest wywoływana w przypadku tymczasowego przerwania łączności, z którego usługa SignalR może automatycznie odzyskiwać dane, na przykład w przypadku tymczasowego odłączenia kabla i ponownego nawiązania połączenia przed przekroczeniem limitu czasu połączenia. Metoda OnDisconnected jest wywoływana, gdy klient jest odłączony, a usługa SignalR nie może automatycznie ponownie nawiązać połączenia, na przykład gdy przeglądarka przejdzie do nowej strony. W związku z tym możliwa sekwencja zdarzeń dla danego klienta to OnConnected, OnReconnected, OnDisconnectedlub OnConnected, OnDisconnected. Dla danego połączenia nie będzie widoczna sekwencja OnConnected, OnDisconnected. OnReconnected

Metoda OnDisconnected nie jest wywoływana w niektórych scenariuszach, takich jak gdy serwer ulegnie awarii lub domena aplikacji zostanie poddana recyklingu. Gdy inny serwer zostanie włączony lub domena aplikacji zakończy odtwarzanie, niektórzy klienci mogą mieć możliwość ponownego nawiązania połączenia i wyzwolenia OnReconnected zdarzenia.

Aby uzyskać więcej informacji, zobacz Opis i obsługa zdarzeń okresu istnienia połączenia w usłudze SignalR.

Stan elementu wywołującego nie został wypełniony

Metody obsługi zdarzeń okresu istnienia połączenia są wywoływane z serwera, co oznacza, że każdy stan umieszczony w state obiekcie na kliencie nie zostanie wypełniony we Caller właściwości na serwerze. Aby uzyskać informacje o state obiekcie i Caller właściwości, zobacz Jak przekazać stan między klientami i klasą Hub w dalszej części tego tematu.

Jak uzyskać informacje o kliencie z właściwości Context

Aby uzyskać informacje o kliencie, użyj Context właściwości klasy Hub. Właściwość Context zwraca obiekt HubCallerContext , który zapewnia dostęp do następujących informacji:

  • Identyfikator połączenia klienta wywołującego.

    string connectionID = Context.ConnectionId;
    

    Identyfikator połączenia jest identyfikatorem GUID przypisanym przez usługę SignalR (nie można określić wartości we własnym kodzie). Istnieje jeden identyfikator połączenia dla każdego połączenia, a ten sam identyfikator połączenia jest używany przez wszystkie centra, jeśli masz wiele centrów w aplikacji.

  • Dane nagłówka HTTP.

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

    Nagłówki HTTP można również pobrać z witryny Context.Headers. Przyczyną wielu odwołań do tej samej rzeczy jest to, że Context.Headers została utworzona jako pierwsza, Context.Request właściwość została dodana później i Context.Headers została zachowana w celu zapewnienia zgodności z poprzednimi wersjami.

  • Wykonywanie zapytań dotyczących danych ciągów.

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

    Możesz również pobrać dane ciągu zapytania z witryny Context.QueryString.

    Parametry zapytania, które otrzymujesz w tej właściwości, to ten, który został użyty z żądaniem HTTP, które nawiązało połączenie usługi SignalR. Parametry parametrów zapytania można dodać w kliencie, konfigurując połączenie, co jest wygodnym sposobem przekazywania danych dotyczących klienta z klienta do serwera. W poniższym przykładzie pokazano jeden ze sposobów dodawania ciągu zapytania w kliencie JavaScript podczas korzystania z wygenerowanego serwera proxy.

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

    Aby uzyskać więcej informacji na temat ustawiania parametrów ciągu zapytania, zobacz przewodniki interfejsu API dla klientów języka JavaScript i platformy .NET .

    Metodę transportu używaną dla połączenia można znaleźć w danych parametrów zapytania wraz z innymi wartościami używanymi wewnętrznie przez usługę SignalR:

    string transportMethod = queryString["transport"];
    

    Wartość transportMethod to "webSockets", "serverSentEvents", "foreverFrame" lub "longPolling". Należy pamiętać, że jeśli ta wartość jest sprawdzana w OnConnected metodzie obsługi zdarzeń, w niektórych scenariuszach początkowo możesz uzyskać wartość transportu, która nie jest ostateczną wynegocjowaną metodą transportu dla połączenia. W takim przypadku metoda zgłosi wyjątek i zostanie wywołana ponownie później po ustanowieniu ostatecznej metody transportu.

  • Pliki cookie.

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

    Możesz również pobrać pliki cookie z witryny Context.RequestCookies.

  • Informacje o użytkowniku.

    System.Security.Principal.IPrincipal user = Context.User;
    
  • Obiekt HttpContext dla żądania :

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

    Użyj tej metody zamiast pobierania HttpContext.CurrentHttpContext obiektu dla połączenia usługi SignalR.

Jak przekazać stan między klientami a klasą Hub

Serwer proxy klienta udostępnia state obiekt, w którym można przechowywać dane, które mają być przesyłane do serwera przy użyciu każdego wywołania metody. Na serwerze można uzyskać dostęp do tych danych we Clients.Caller właściwości w metodach centrum, które są wywoływane przez klientów. Właściwość Clients.Caller nie jest wypełniana dla metod OnConnectedobsługi zdarzeń okresu istnienia połączenia , OnDisconnectedi OnReconnected.

Tworzenie lub aktualizowanie danych w state obiekcie i Clients.Caller właściwość działa w obu kierunkach. Możesz zaktualizować wartości na serwerze i są one przekazywane z powrotem do klienta.

W poniższym przykładzie pokazano kod klienta JavaScript, który przechowuje stan transmisji na serwer przy użyciu każdego wywołania metody.

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

W poniższym przykładzie pokazano równoważny kod w kliencie platformy .NET.

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

W klasie Hub możesz uzyskać dostęp do tych danych we Clients.Caller właściwości . W poniższym przykładzie pokazano kod, który pobiera stan określony w poprzednim przykładzie.

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

Uwaga

Ten mechanizm utrwalania stanu nie jest przeznaczony dla dużych ilości danych, ponieważ wszystko, co zostało umieszczone w state obiekcie lub Clients.Caller , jest zaokrąglone przy każdym wywołaniu metody. Jest to przydatne w przypadku mniejszych elementów, takich jak nazwy użytkowników lub liczniki.

Jak obsługiwać błędy w klasie Hub

Aby obsłużyć błędy występujące w metodach klasy Centrum, użyj jednej lub obu następujących metod:

  • Opakuj kod metody w blokach try-catch i zarejestruj obiekt wyjątku. Do celów debugowania można wysłać wyjątek do klienta, ale ze względów bezpieczeństwa wysyłanie szczegółowych informacji do klientów w środowisku produkcyjnym nie jest zalecane.

  • Utwórz moduł potoku usługi Hubs, który obsługuje metodę OnIncomingError . W poniższym przykładzie pokazano moduł potoku, który rejestruje błędy, a następnie kod w pliku Global.asax, który wprowadza moduł do potoku 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();
    }
    

Aby uzyskać więcej informacji na temat modułów potoku centrum, zobacz Jak dostosować potok hubs w dalszej części tego tematu.

Jak włączyć śledzenie

Aby włączyć śledzenie po stronie serwera, dodaj element system.diagnostics do pliku Web.config, jak pokazano w tym przykładzie:

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

Po uruchomieniu aplikacji w programie Visual Studio możesz wyświetlić dzienniki w oknie Dane wyjściowe .

Jak wywoływać metody klienta i zarządzać grupami spoza klasy Hub

Aby wywołać metody klienta z innej klasy niż klasa centrum, uzyskaj odwołanie do obiektu kontekstu usługi SignalR dla centrum i użyj jej do wywoływania metod na kliencie lub zarządzania grupami.

Następująca przykładowa StockTicker klasa pobiera obiekt kontekstu, przechowuje go w wystąpieniu klasy, przechowuje wystąpienie klasy we właściwości statycznej i używa kontekstu z wystąpienia klasy pojedynczej klasy do wywołania updateStockPrice metody na klientach połączonych z centrum o nazwie 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);
            }
        }
    }

Jeśli musisz używać kontekstu wiele razy w długotrwałym obiekcie, pobierz odwołanie raz i zapisz go zamiast ponownie za każdym razem. Pobranie kontekstu raz gwarantuje, że usługa SignalR wysyła komunikaty do klientów w tej samej sekwencji, w której metody centrum tworzą wywołania metody klienta. Aby zapoznać się z samouczkiem pokazującym, jak używać kontekstu usługi SignalR dla koncentratora, zobacz Server Broadcast with ASP.NET SignalR (Emisja serwera za pomocą usługi SignalR ASP.NET).

Wywoływanie metod klienta

Można określić, którzy klienci otrzymają RPC, ale masz mniej opcji niż w przypadku wywołania z klasy Centrum. Jest to spowodowane tym, że kontekst nie jest skojarzony z określonym wywołaniem od klienta, więc wszystkie metody wymagające znajomości bieżącego identyfikatora połączenia, takie jak Clients.Others, lub Clients.Caller, Clients.OthersInGroupnie są dostępne. Dostępne są następujące opcje:

  • Wszyscy połączeni klienci.

    context.Clients.All.addContosoChatMessageToPage(name, message);
    
  • Określony klient zidentyfikowany przez identyfikator połączenia.

    context.Clients.Client(connectionID).addContosoChatMessageToPage(name, message);
    
  • Wszyscy połączeni klienci z wyjątkiem określonych klientów identyfikowanych przez identyfikator połączenia.

    context.Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);
    
  • Wszyscy połączeni klienci w określonej grupie.

    context.Clients.Group(groupName).addContosoChatMessageToPage(name, message);
    
  • Wszyscy połączeni klienci w określonej grupie z wyjątkiem określonych klientów identyfikowanych przez identyfikator połączenia.

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

Jeśli wywołujesz klasę inną niż Hub z metod w klasie Hub, możesz przekazać bieżący identyfikator połączenia i użyć go w metodzie , lub , lub Clients.GroupClients.OthersClients.CallerClients.OthersInGroup. Clients.AllExceptClients.Client W poniższym przykładzie klasa przekazuje identyfikator połączenia do klasy, MoveShapeHub aby klasa mogła symulować Clients.Othersklasę Broadcaster .Broadcaster

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

Zarządzanie członkostwem w grupie

W przypadku zarządzania grupami masz te same opcje co w klasie Hub.

  • Dodawanie klienta do grupy

    context.Groups.Add(connectionID, groupName);
    
  • Usuwanie klienta z grupy

    context.Groups.Remove(connectionID, groupName);
    

Jak dostosować potok Hubs

Usługa SignalR umożliwia wstrzyknięcie własnego kodu do potoku centrum. W poniższym przykładzie pokazano moduł potoku niestandardowego koncentratora, który rejestruje każde przychodzące wywołanie metody odebrane od klienta i metody wychodzącej wywoływane na kliencie:

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

Następujący kod w pliku Global.asax rejestruje moduł do uruchomienia w potoku centrum:

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

Istnieje wiele różnych metod, które można zastąpić. Aby uzyskać pełną listę, zobacz Metody HubPipelineModule.