Подключение устройств с помощью удаленных сеансов

Функция удаленных сеансов позволяет приложениям подключаться к другим устройствам с помощью сеанса для явного обмена сообщениями между приложениями или для обмена управляемыми системой данными через брокер, например SpatialEntityStore для совместного использования голографических объектов на устройствах Windows Holographic.

Удаленные сеансы может создать любое устройство с Windows, а также любое такое устройство может запросить разрешение на присоединение (хотя можно сделать сеансы видимыми только по приглашению), включая устройства, вход на которых выполнен другими пользователями. В этом руководстве представлен базовый пример кода для всех основных сценариев с использованием удаленных сеансов. Этот код можно встроить в существующий проект приложения и изменять по необходимости. Пример комплексной реализации см. в примере приложения Quiz Game).

Предварительная настройка

Добавление возможности remoteSystem

Чтобы приложение могло запускать другое приложение на удаленном устройстве, вам потребуется добавить возможность remoteSystem в манифест пакета приложений. Вы можете использовать конструктор манифеста пакета, чтобы добавить его, выбрав Удаленная система на вкладке Возможности или вручную добавить следующую строку в файл Package.appxmanifest проекта.

<Capabilities>
   <uap3:Capability Name="remoteSystem"/>
</Capabilities>

Включение обнаружения между пользователями на устройстве

Функция удаленных сеансов ориентирована на подключение нескольких разных пользователей, поэтому на участвующих в сеансе устройствах должна быть включена функция общего доступа между пользователями. Это параметр системы, который можно запросить с помощью статического метода в классе RemoteSystem:

if (!RemoteSystem.IsAuthorizationKindEnabled(RemoteSystemAuthorizationKind.Anonymous)) {
	// The system is not authorized to connect to cross-user devices. 
	// Inform the user that they can discover more devices if they
	// update the setting to "Everyone nearby".
}

Чтобы изменить этот параметр, пользователь должен открыть приложение Параметры. В менюОбщий> доступ к системным>интерфейсам на разных устройствах есть раскрывающийся список, в котором пользователь может указать устройства, с которыми может предоставляться общий доступ.

страница параметров общих возможностей

Включение необходимых пространств имен

Для использования всех фрагментов кода в этом руководстве вам потребуются следующие операторы using в файлах классов.

using System.Runtime.Serialization.Json;
using Windows.Foundation.Collections;
using Windows.System.RemoteSystems;

Создание удаленного сеанса

Для создания экземпляра удаленного сеанса необходимо начать с объекта RemoteSystemSessionController. Используйте следующую платформу для создания нового сеанса и обработки запросов на присоединение от других устройств.

public async void CreateSession() {
    
    // create a session controller
    RemoteSystemSessionController manager = new RemoteSystemSessionController("Bob’s Minecraft game");
    
    // register the following code to handle the JoinRequested event
    manager.JoinRequested += async (sender, args) => {
        // Get the deferral
        var deferral = args.GetDeferral();
        
        // display the participant (args.JoinRequest.Participant) on UI, giving the 
        // user an opportunity to respond
        // ...
        
        // If the user chooses "accept", accept this remote system as a participant
        args.JoinRequest.Accept();
    };
    
    // create and start the session
    RemoteSystemSessionCreationResult createResult = await manager.CreateSessionAsync();
    
    // handle the creation result
    if (createResult.Status == RemoteSystemSessionCreationStatus.Success) {
        // creation was successful, get a reference to the session
        RemoteSystemSession currentSession = createResult.Session;
        
        // optionally subscribe to the disconnection event
        currentSession.Disconnected += async (sender, args) => {
            // update the UI, using args.Reason
            //...
        };
    
        // Use session (see later section)
        //...
    
    } else if (createResult.Status == RemoteSystemSessionCreationStatus.SessionLimitsExceeded) {
        // creation failed. Optionally update UI to indicate that there are too many sessions in progress
    } else {
        // creation failed for an unknown reason. Optionally update UI
    }
}

Создание удаленного сеанса с доступом только по приглашению

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

Эта процедура практически идентична указанной выше, но при создании экземпляра RemoteSystemSessionController следует передать настроенный объект RemoteSystemSessionOptions.

// define the session options with the invite-only designation
RemoteSystemSessionOptions sessionOptions = new RemoteSystemSessionOptions();
sessionOptions.IsInviteOnly = true;

// create the session controller
RemoteSystemSessionController manager = new RemoteSystemSessionController("Bob's Minecraft game", sessionOptions);

//...

Для отправки приглашения необходима ссылка на принимающую удаленную систему (получается путем обычного обнаружения удаленной системы). Передайте эту ссылку методу SendInvitationAsync объекта сеанса. Все участники сеанса получают ссылку на удаленный сеанс (см. следующий раздел), поэтому любой участник может отправить приглашение.

// "currentSession" is a reference to a RemoteSystemSession.
// "guestSystem" is a previously discovered RemoteSystem instance
currentSession.SendInvitationAsync(guestSystem); 

Обнаружение удаленного сеанса и присоединение к нему

Процесс обнаружения удаленных сеансов обрабатывается классом RemoteSystemSessionWatcher и аналогичен обнаружению отдельных удаленных систем.

public void DiscoverSessions() {
    
    // create a watcher for remote system sessions
    RemoteSystemSessionWatcher sessionWatcher = RemoteSystemSession.CreateWatcher();
    
    // register a handler for the "added" event
    sessionWatcher.Added += async (sender, args) => {
        
        // get a reference to the info about the discovered session
        RemoteSystemSessionInfo sessionInfo = args.SessionInfo;
        
        // Optionally update the UI with the sessionInfo.DisplayName and 
        // sessionInfo.ControllerDisplayName strings. 
        // Save a reference to this RemoteSystemSessionInfo to use when the
        // user selects this session from the UI
        //...
    };
    
    // Begin watching
    sessionWatcher.Start();
}

При получении экземпляра RemoteSystemSessionInfo его можно использовать, чтобы отправить устройству, управляющему соответствующим сеансом, запрос на присоединение. Принятый запрос на присоединение асинхронно вернет объект RemoteSystemSessionJoinResult, содержащий ссылку на соответствующий сеанс.

public async void JoinSession(RemoteSystemSessionInfo sessionInfo) {

    // issue a join request and wait for result.
    RemoteSystemSessionJoinResult joinResult = await sessionInfo.JoinAsync();
    if (joinResult.Status == RemoteSystemSessionJoinStatus.Success) {
        // Join request was approved

        // RemoteSystemSession instance "currentSession" was declared at class level.
        // Assign the value obtained from the join result.
        currentSession = joinResult.Session;
        
        // note connection and register to handle disconnection event
        bool isConnected = true;
        currentSession.Disconnected += async (sender, args) => {
            isConnected = false;

            // update the UI with args.Reason value
        };
        
        if (isConnected) {
            // optionally use the session here (see next section)
            //...
        }
    } else {
        // Join was unsuccessful.
        // Update the UI, using joinResult.Status value to show cause of failure.
    }
}

Устройство может присоединиться к нескольким сеансам одновременно. По этой причине следует отделить функции присоединения от фактического взаимодействия с каждым сеансом. Если в приложении есть ссылка на экземпляр RemoteSystemSession, через этот сеанс можно обмениваться данными.

Обмен сообщениями и данными через удаленный сеанс

Получение сообщений

Обмениваться сообщениями и данными с другими участвующими в сеансе устройствами можно с помощью экземпляра RemoteSystemSessionMessageChannel, который представляет единый канал обмена данными для всего сеанса. Сразу после инициализации он начинает прослушивать входящие сообщения.

Примечание

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

public async void StartReceivingMessages() {
    
    // Initialize. The channel name must be known by all participant devices 
    // that will communicate over it.
    RemoteSystemSessionMessageChannel messageChannel = new RemoteSystemSessionMessageChannel(currentSession, 
        "Everyone in Bob's Minecraft game", 
        RemoteSystemSessionMessageChannelReliability.Reliable);
    
    // write the handler for incoming messages on this channel
    messageChannel.ValueSetReceived += async (sender, args) => {
        
        // Update UI: a message was received from the participant args.Sender
        
        // Deserialize the message 
        // (this app must know what key to use and what object type the value is expected to be)
        ValueSet receivedMessage = args.Message;
        object rawData = receivedMessage["appKey"]);
        object value = new ExpectedType(); // this must be whatever type is expected

        using (var stream = new MemoryStream((byte[])rawData)) {
            value = new DataContractJsonSerializer(value.GetType()).ReadObject(stream);
        }
        
        // do something with the "value" object
        //...
    };
}

Отправка сообщений

После установки канала можно с легкостью отправлять сообщения всем участникам сеанса.

public async void SendMessageToAllParticipantsAsync(RemoteSystemSessionMessageChannel messageChannel, object value){

    // define a ValueSet message to send
    ValueSet message = new ValueSet();
    
    // serialize the "value" object to send
    using (var stream = new MemoryStream()){
        new DataContractJsonSerializer(value.GetType()).WriteObject(stream, value);
        byte[] rawData = stream.ToArray();
            message["appKey"] = rawData;
    }
    
    // Send message to all participants. Ordering is not guaranteed.
    await messageChannel.BroadcastValueSetAsync(message);
}

Для отправки сообщения только определенным участникам необходимо инициализировать процесс обнаружения, чтобы получить ссылки на участвующие в сеансе удаленные системы. Эта процедура аналогична процессу обнаружения удаленных систем вне сеанса. Используйте экземпляр RemoteSystemSessionParticipantWatcher для поиска участвующих в сеансе устройств.

public void WatchForParticipants() {
    // "currentSession" is a reference to a RemoteSystemSession.
    RemoteSystemSessionParticipantWatcher watcher = currentSession.CreateParticipantWatcher();

    watcher.Added += (sender, participant) => {
        // save a reference to "participant"
        // optionally update UI
    };   

    watcher.Removed += (sender, participant) => {
        // remove reference to "participant"
        // optionally update UI
    };

    watcher.EnumerationCompleted += (sender, args) => {
        // Apps can delay data model render up until this point if they wish.
    };

    // Begin watching for session participants
    watcher.Start();
}

После получения списка ссылок на участников сеанса можно отправить сообщение любому их набору.

Для отправки сообщения одному участнику (желательно, выбранному пользователем на экране) передайте ссылку методу, как показано ниже.

public async void SendMessageToParticipantAsync(RemoteSystemSessionMessageChannel messageChannel, RemoteSystemSessionParticipant participant, object value) {
    
    // define a ValueSet message to send
    ValueSet message = new ValueSet();
    
    // serialize the "value" object to send
    using (var stream = new MemoryStream()){
        new DataContractJsonSerializer(value.GetType()).WriteObject(stream, value);
        byte[] rawData = stream.ToArray();
            message["appKey"] = rawData;
    }

    // Send message to the participant
    await messageChannel.SendValueSetAsync(message,participant);
}

Для отправки сообщения нескольким участникам (желательно, выбранному пользователем на экране) добавьте их в объект списка и передайте этот список методу, как показано ниже.

public async void SendMessageToListAsync(RemoteSystemSessionMessageChannel messageChannel, IReadOnlyList<RemoteSystemSessionParticipant> myTeam, object value){

    // define a ValueSet message to send
    ValueSet message = new ValueSet();
    
    // serialize the "value" object to send
    using (var stream = new MemoryStream()){
        new DataContractJsonSerializer(value.GetType()).WriteObject(stream, value);
        byte[] rawData = stream.ToArray();
            message["appKey"] = rawData;
    }

    // Send message to specific participants. Ordering is not guaranteed.
    await messageChannel.SendValueSetToParticipantsAsync(message, myTeam);   
}