Путеводитель по API концентраторов SignalR ASP.NET — клиент .NET (SignalR 1. x)

Патрик Флетчера, Tom Dykstra)

Warning

Эта документация не относится к последней версии SignalR. Ознакомьтесь с ASP.NET Core SignalR.

В этом документе содержатся общие сведения об использовании API концентраторов для SignalR версии 2 в клиентах .NET, таких как Магазин Windows (WinRT), WPF, Silverlight и консольные приложения.

API концентраторов SignalR позволяет выполнять удаленные вызовы процедур (RPC) с сервера на подключенные клиенты и с клиентов на сервер. В серверном коде определяются методы, которые могут вызываться клиентами, и вызываются методы, которые выполняются на клиенте. В клиентском коде определяются методы, которые могут быть вызваны с сервера, а также вызываются методы, которые выполняются на сервере. Этот механизм отвечает за все клиентские коммуникации.

SignalR также предлагает интерфейс API более низкого уровня, называемый постоянными подключениями. Общие сведения о SignalR, концентраторах и постоянных подключениях, а также руководство, в котором показано, как создать полноценное приложение SignalR, см. в разделе SignalR-начало работы.

Обзор

Этот документ содержит следующие разделы.

Примеры клиентских проектов .NET см. в следующих ресурсах:

Документацию по программированию клиентов сервера или JavaScript см. в следующих ресурсах:

Ссылки на справочные статьи по API относятся к версии .NET 4,5 API. Если вы используете .NET 4, см. статьи с описанием API для .NET 4.

Настройка клиента

Установите пакет NuGet Microsoft. ASPNET. SignalR. Client (не пакет Microsoft. ASPNET. SignalR ). Этот пакет поддерживает клиентские приложения WinRT, Silverlight, WPF, консольное приложение и Windows Phone для .NET 4 и .NET 4,5.

Если версия SignalR, установленная на клиенте, отличается от версии на сервере, то SignalR часто может адаптироваться к разнице. Например, когда выдается SignalR версии 2,0 и устанавливается на сервере, сервер будет поддерживать клиенты, на которых установлен выпуск 1.1. x, а также клиенты, на которых установлено 2,0. Если разница между версией на сервере и версией на клиенте слишком велика, SignalR вызывает исключение InvalidOperationException, когда клиент пытается установить соединение. Сообщение об ошибке: "You are using a version of the client that isn't compatible with the server. Client version X.X, server version X.X".

Как установить соединение

Прежде чем установить соединение, необходимо создать объект HubConnection и создать прокси-сервер. Чтобы установить соединение, вызовите метод Start для объекта HubConnection.

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();

Note

Для клиентов JavaScript необходимо зарегистрировать по крайней мере один обработчик событий перед вызовом метода Start для установления соединения. Это не является обязательным для клиентов .NET. Для клиентов JavaScript созданный код прокси автоматически создает учетные записи-посредники для всех концентраторов, существующих на сервере, и регистрирует обработчик, чтобы указать, какие концентраторы клиент планирует использовать. Но для клиента .NET вы создаете прокси-серверы концентратора вручную, поэтому SignalR предполагает, что вы будете использовать любой центр, для которого создается прокси.

В примере кода для подключения к службе SignalR используется URL-адрес по умолчанию "/SignalR". Сведения о том, как указать другой базовый URL-адрес, см. в разделе ASP.NET SignalR Hub API Guide-Server-URL-адрес/SignalR.

Метод Start выполняется асинхронно. Чтобы следующие строки кода не выполнялись до тех пор, пока соединение не будет установлено, используйте await в асинхронном методе ASP.NET 4,5 или .Wait() в синхронном методе. Не используйте .Wait() в клиенте WinRT.

await connection.Start();
connection.Start().Wait();

Класс HubConnection является потокобезопасным.

Междоменные соединения от клиентов Silverlight

Сведения о том, как включить междоменные подключения от клиентов Silverlight, см. в разделе обеспечение доступности службы через границы домена.

Настройка подключения

Прежде чем устанавливать соединение, можно указать любой из следующих параметров.

  • Ограничение количества одновременных подключений.
  • Параметры строки запроса.
  • Метод перевозки.
  • Заголовки HTTP.
  • Сертификаты клиента.

Установка максимального числа одновременных подключений в клиентах WPF

В клиентах WPF может потребоваться увеличить максимальное число одновременных подключений со значением по умолчанию 2. Рекомендуемое значение — 10.

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
ServicePointManager.DefaultConnectionLimit = 10;
await hubConnection.Start();

Дополнительные сведения см. в разделе ServicePointManager. DefaultConnectionLimit.

Указание параметров строки запроса

Если требуется отправлять данные на сервер при подключении клиента, можно добавить параметры строки запроса в объект соединения. В следующем примере показано, как задать параметр строки запроса в клиентском коде.

var querystringData = new Dictionary<string, string>();
querystringData.Add("contosochatversion", "1.0");
var connection = new HubConnection("http://contoso.com/", querystringData);

В следующем примере показано, как считать параметр строки запроса в серверном коде.

public class StockTickerHub : Hub
{
    public override Task OnConnected()
    {
        var version = Context.QueryString["contosochatversion"];
        if (version != "1.0")
        {
            Clients.Caller.notifyWrongVersion();
        }
        return base.OnConnected();
    }
}

Как указать метод перевозки

В рамках процесса подключения клиент SignalR обычно согласовывается с сервером для определения наилучшего транспорта, поддерживаемого как сервером, так и клиентом. Если вы уже уверены, какой транспорт вы хотите использовать, можно обойти этот процесс согласования. Чтобы указать транспортный метод, передайте объект транспорта в метод Start. В следующем примере показано, как указать метод перевозки в клиентском коде.

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start(new LongPollingTransport());

Пространство имен Microsoft. AspNet. SignalR. Client. Transports содержит следующие классы, которые можно использовать для указания транспорта.

Транспорт Фореверфраме не включен в этот список, так как он используется только браузерами.

Сведения о том, как проверить транспортный метод в коде сервера, см. в разделе ASP.NET SignalR Hub API Guide-Server-как получить сведения о клиенте из свойства Context. Дополнительные сведения о транспортировках и резервных запасах см. в статье Введение в SignalR-транспорты и резервные стратегии.

Как указать заголовки HTTP

Чтобы задать заголовки HTTP, используйте свойство Headers объекта Connection. В следующем примере показано, как добавить заголовок HTTP.

hubConnection = new hubConnection("http://www.contoso.com/");
connection.Headers.Add("headername", "headervalue");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await connection.Start();

Как указать сертификаты клиента

Чтобы добавить сертификаты клиента, используйте метод AddClientCertificate объекта Connection.

hubConnection = new hubConnection("http://www.contoso.com/");
hubConnection.AddClientCertificate(X509Certificate.CreateFromCertFile("MyCert.cer"));
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await connection.Start();

Создание прокси-сервера концентратора

Чтобы определить методы на клиенте, которые концентратор может вызывать с сервера, и вызывать методы в концентраторе на сервере, создайте прокси-сервер для концентратора, вызвав CreateHubProxy для объекта Connection. Строка, которую вы передаете CreateHubProxy, является именем класса концентратора или именем, заданным атрибутом HubName, если он использовался на сервере. Сопоставление имен не зависит от регистра.

Класс Hub на сервере

public class StockTickerHub : Hub

Создание прокси клиента для класса HUB

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();

Если класс Hub дополнить атрибутом HubName, используйте это имя.

Класс Hub на сервере

[HubName("stockTicker")]
public class StockTickerHub : Hub

Создание прокси клиента для класса HUB

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("stockTicker");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();

Прокси-объект является потокобезопасным. На самом деле, если вы вызываете HubConnection.CreateHubProxy несколько раз с одним и тем же hubName, вы получаете один кэшированный объект IHubProxy.

Определение методов на клиенте, который может вызывать сервер

Чтобы определить метод, который может вызвать сервер, используйте метод On прокси-сервера для регистрации обработчика событий.

Сопоставление имен методов не учитывает регистр. Например, Clients.All.UpdateStockPrice на сервере будет выполнять updateStockPrice, updatestockpriceили UpdateStockPrice на клиенте.

Разные клиентские платформы имеют разные требования к написанию кода метода для обновления пользовательского интерфейса. Приведенные примеры предназначены для клиентов WinRT (Windows Store .NET). Примеры приложений WPF, Silverlight и консольного приложения приведены в отдельном разделе далее в этом разделе.

Методы без параметров

Если обрабатываемый метод не имеет параметров, используйте неуниверсальную перегрузку метода On:

Код сервера, вызывающий клиентский метод без параметров

public class StockTickerHub : Hub
{
    public void NotifyAllClients()
    {
         Clients.All.Notify();
    }
}

Клиентский код WinRT для метода, вызываемого с сервера без параметров (см. примеры WPF и Silverlight далее в этом разделе)

var hubConnection = new HubConnection("http://www.contoso.com/");
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHub.On("notify", () =>
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += "Notified!\n";
    }, null)
);
await hubConnection.Start();

Методы с параметрами, указание типов параметров

Если обрабатываемый метод имеет параметры, укажите типы параметров в качестве универсальных типов метода On. Существуют универсальные перегрузки метода On, позволяющие указать до 8 параметров (4 в Windows Phone 7). В следующем примере один параметр отправляется в метод UpdateStockPrice.

Серверный код, вызывающий клиентский метод с параметром

public void BroadcastStockPrice(Stock stock)
{
    context.Clients.Others.UpdateStockPrice(stock);
}

Класс акции, используемый для параметра

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

Клиентский код WinRT для метода, вызываемого с сервера с параметром (см. примеры WPF и Silverlight далее в этом разделе)

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Методы с параметрами, указание динамических объектов для параметров

В качестве альтернативы указанию параметров в качестве универсальных типов метода On можно указать параметры как динамические объекты:

Серверный код, вызывающий клиентский метод с параметром

public void BroadcastStockPrice(Stock stock)
{
    context.Clients.Others.UpdateStockPrice(stock);
}

Класс акции, используемый для параметра

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

Клиентский код WinRT для метода, вызываемого с сервера с параметром, с использованием динамического объекта для параметра (см. примеры WPF и Silverlight далее в этом разделе)

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Удаление обработчика

Чтобы удалить обработчик, вызовите его метод Dispose.

Клиентский код для метода, вызываемого с сервера

var updateStockPriceHandler = stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Код клиента для удаления обработчика

updateStockPriceHandler.Dispose();

Вызов методов сервера из клиента

Чтобы вызвать метод на сервере, используйте метод Invoke для прокси-сервера концентратора.

Если метод сервера не имеет возвращаемого значения, используйте неуниверсальную перегрузку метода Invoke.

Серверный код для метода, не имеющего возвращаемого значения

public class StockTickerHub : Hub
{
    public void JoinGroup(string groupName)
    {
        Groups.Add(Context.ConnectionId, groupName); 
    }
}

Клиентский код, вызывающий метод, не имеющий возвращаемого значения

stockTickerHubProxy.Invoke("JoinGroup", hubConnection.ConnectionID, "SignalRChatRoom");

Если метод сервера имеет возвращаемое значение, укажите тип возвращаемого значения в качестве универсального типа метода Invoke.

Серверный код для метода, который имеет возвращаемое значение и принимает параметр сложного типа

public IEnumerable<Stock> AddStock(Stock stock)
{
    _stockTicker.AddStock(stock);
    return _stockTicker.GetAllStocks();
}

Класс акции, используемый для параметра и возвращаемого значения

public class Stock
{
    public string Symbol { get; set; }
    public decimal Price { get; set; }
}

Клиентский код, вызывающий метод с возвращаемым значением и принимающий параметр сложного типа в асинхронном методе ASP.NET 4,5

var stocks = await stockTickerHub.Invoke<IEnumerable<Stock>>("AddStock", new Stock() { Symbol = "MSFT" });
foreach (Stock stock in stocks)
{
    textBox.Text += string.Format("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price);
}

Клиентский код, вызывающий метод с возвращаемым значением и принимающий параметр сложного типа в синхронном методе

var stocks = stockTickerHub.Invoke<IEnumerable<Stock>>("AddStock", new Stock() { Symbol = "MSFT" }).Result;
foreach (Stock stock in stocks)
{
    textBox.Text += string.Format("Symbol: {0} price: {1}\n", stock.Symbol, stock.Price);
}

Метод Invoke выполняется асинхронно и возвращает объект Task. Если не указать await или .Wait(), следующая строка кода будет выполняться до завершения выполнения метода, который вызывается.

Как работать с событиями времени жизни соединения

SignalR предоставляет следующие события времени жизни подключения, которые можно выполнять:

  • Received: возникает, когда в соединении получены какие-либо данные. Предоставляет полученные данные.
  • ConnectionSlow: возникает, когда клиент обнаруживает слишком большое или частое удаление соединения.
  • Reconnecting: возникает, когда начинается повторное подключение базового транспорта.
  • Reconnected: возникает при повторном подключении базового транспорта.
  • StateChanged: возникает при изменении состояния соединения. Предоставляет старое состояние и новое состояние. Сведения о значениях состояния соединения см. в разделе перечисление ConnectionState.
  • Closed: возникает при отключении соединения.

Например, если требуется отображать предупреждающие сообщения об ошибках, которые не являются критическими, но вызывают периодически возникающих проблем с подключением, например медленная работа или частым удалением соединения, обработайте событие ConnectionSlow.

hubConnection.ConnectionSlow += () => Console.WriteLine("Connection problems.");

Дополнительные сведения см. в разделе Основные сведения и обработка событий времени жизни подключения в SignalR.

Как обрабатывались ошибки

Если на сервере явно не включены подробные сообщения об ошибках, то объект исключения, возвращаемый SignalR после ошибки, содержит минимальные сведения об ошибке. Например, если вызов newContosoChatMessage завершается ошибкой, сообщение об ошибке в объекте Error содержит "There was an error invoking Hub method 'contosoChatHub.newContosoChatMessage'." Отправка подробных сообщений об ошибках клиентам в рабочей среде не рекомендуется по соображениям безопасности, но если вы хотите включить подробные сообщения об ошибках для устранения неполадок, используйте следующий код на сервере.

var hubConfiguration = new HubConfiguration();
hubConfiguration.EnableDetailedErrors = true;
RouteTable.Routes.MapHubs(hubConfiguration);

Для обработки ошибок, вызванных SignalR, можно добавить обработчик для события Error в объекте Connection.

hubConnection.Error += ex => Console.WriteLine("SignalR error: {0}", ex.Message);

Чтобы обрабатывались ошибки вызовов методов, заключите код в блок try-catch.

try
{
    IEnumerable<Stock> stocks = await stockTickerHub.Invoke<IEnumerable<Stock>>("GetAllStocks");
    foreach (Stock stock in stocks)
    {
        Console.WriteLine("Symbol: {0} price: {1}", stock.Symbol, stock.Price);
    }
}
catch (Exception ex)
{
    Console.WriteLine("Error invoking GetAllStocks: {0}", ex.Message);
}

Как включить ведение журнала на стороне клиента

Чтобы включить ведение журнала на стороне клиента, задайте свойства TraceLevel и TraceWriter объекта соединения.

var hubConnection = new HubConnection("http://www.contoso.com/");
hubConnection.TraceLevel = TraceLevels.All;
hubConnection.TraceWriter = Console.Out;
IHubProxy stockTickerHubProxy = hubConnection.CreateHubProxy("StockTickerHub");
stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => Console.WriteLine("Stock update for {0} new price {1}", stock.Symbol, stock.Price));
await hubConnection.Start();

Примеры кода приложений WPF, Silverlight и консольного приложения для клиентских методов, которые может вызывать сервер

Примеры кода, показанные выше, для определения клиентских методов, которые сервер может вызывать для клиентов WinRT. В следующих примерах показан эквивалентный код для клиентов WPF, Silverlight и консольных приложений.

Методы без параметров

Клиентский код WPF для метода, вызываемого с сервера без параметров

stockTickerHub.On<Stock>("notify", () =>
    Dispatcher.InvokeAsync(() =>
        {
            SignalRTextBlock.Text += string.Format("Notified!");
        })
);

Клиентский код Silverlight для метода, вызываемого с сервера без параметров

stockTickerHub.On<Stock>("notify", () =>
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += "Notified!";
    }, null)
);

Код клиента консольного приложения для метода, вызываемого с сервера без параметров

stockTickerHubProxyProxy.On("Notify", () => Console.WriteLine("Notified!"));

Методы с параметрами, указание типов параметров

Клиентский код WPF для метода, вызываемого с сервера с параметром

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Dispatcher.InvokeAsync(() =>
        {
            textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
        })
);

Клиентский код Silverlight для метода, вызываемого с сервера с параметром

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Код клиента консольного приложения для метода, вызываемого с сервера с параметром

stockTickerHubProxy.On<Stock>("UpdateStockPrice", stock => 
    Console.WriteLine("Symbol {0} Price {1}", stock.Symbol, stock.Price));

Методы с параметрами, указание динамических объектов для параметров

Клиентский код WPF для метода, вызываемого с сервера с параметром, с использованием динамического объекта для параметра

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    Dispatcher.InvokeAsync(() =>
        {
            textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
        })
);

Клиентский код Silverlight для метода, вызываемого с сервера с параметром, с использованием динамического объекта для параметра

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    // Context is a reference to SynchronizationContext.Current
    Context.Post(delegate
    {
        textBox.Text += string.Format("Stock update for {0} new price {1}\n", stock.Symbol, stock.Price);
    }, null)
);

Код клиента консольного приложения для метода, вызываемого с сервера с параметром, с использованием динамического объекта для параметра

stockTickerHubProxy.On("UpdateStockPrice", stock => 
    Console.WriteLine("Symbol {0} Price {1}", stock.Symbol, stock.Price));