ASP.NET Руководство по API Центров SignalR — клиент .NET (SignalR 1.x)

Патрик Флетчер (Patrick Fletcher),Том Дайкстра (Tom Dykstra)

Предупреждение

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

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

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

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

Общие сведения

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

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

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

Ссылки на справочные статьи по API относятся к версии API для .NET 4.5. Если вы используете .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();

Примечание

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

В примере кода используется URL-адрес по умолчанию "/signalr" для подключения к службе SignalR. Сведения о том, как указать другой базовый URL-адрес, см . в разделе Руководство по API центров SignalR ASP.NET — сервер — 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 включает следующие классы, которые можно использовать для указания транспорта.

  • LongPollingTransport
  • ServerSentEventsTransport
  • WebSocketTransport (доступно, только если и сервер, и клиент используют .NET 4.5.
  • AutoTransport (Автоматически выбирает оптимальный транспорт, поддерживаемый как клиентом, так и сервером. Это транспорт по умолчанию. Передача этого параметра в Start метод имеет тот же эффект, что и не передает ничего.)

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

Сведения о том, как проверка метод транспорта в коде сервера, см. в разделе Руководство по API центров SignalR ASP.NET — сервер — как получить сведения о клиенте из свойства Context. Дополнительные сведения о транспортах и резервных компонентах см. в разделе Общие сведения о SignalR — транспорты и резервные варианты.

Указание заголовков HTTP

Чтобы задать заголовки HTTP, используйте Headers свойство объекта подключения. В следующем примере показано, как добавить заголовок 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 метод в объекте подключения.

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 для объекта соединения. Строка, в CreateHubProxy которую вы передаете , — это имя класса концентратора или имя, указанное HubName атрибутом , если он использовался на сервере. Сопоставление имен не зависит от регистра.

Класс концентратора на сервере

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 , используйте это имя.

Класс концентратора на сервере

[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 (.NET для Магазина Windows). Примеры приложений 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);
}

Класс 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);
}

Класс 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();
}

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

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 ошибкой, сообщение об ошибке в объекте ошибки содержит сообщение "There was an error invoking Hub method 'contosoChatHub.newContosoChatMessage'." Отправка подробных сообщений об ошибках клиентам в рабочей среде не рекомендуется по соображениям безопасности, но если вы хотите включить подробные сообщения об ошибках для устранения неполадок, используйте следующий код на сервере.

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

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

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