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

Функция удаленных сеансов позволяет приложениям подключаться к другим устройствам с помощью сеанса для явного обмена сообщениями между приложениями или для обмена управляемыми системой данными через брокер, например SpatialEntityStore для совместного использования голографических объектов на устройствах Windows Holographic.The Remote Sessions feature allows an app to connect to other devices through a session, either for explicit app messaging or for brokered exchange of system-managed data, such as the SpatialEntityStore for holographic sharing between Windows Holographic devices.

Удаленные сеансы может создать любое устройство с Windows, а также любое такое устройство может запросить разрешение на присоединение (хотя можно сделать сеансы видимыми только по приглашению), включая устройства, вход на которых выполнен другими пользователями.Remote sessions can be created by any Windows device, and any Windows device can request to join (although sessions can have invite-only visibility), including devices signed in by other users. В этом руководстве представлен базовый пример кода для всех основных сценариев с использованием удаленных сеансов.This guide provides basic sample code for all of the major scenarios that make use of remote sessions. Этот код можно встроить в существующий проект приложения и изменять по необходимости.This code can be incorporated into an existing app project and modified as necessary. Пример комплексной реализации см. в примере приложения Quiz Game).For an end-to-end implementation, see the Quiz Game sample app).

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

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

Чтобы приложение могло запускать другое приложение на удаленном устройстве, вам потребуется добавить возможность remoteSystem в манифест пакета приложений.In order for your app to launch an app on a remote device, you must add the remoteSystem capability to your app package manifest. Можно использовать конструктор манифестов пакетов, чтобы добавить его, выбрав удаленная система на вкладке возможности , или вручную добавить следующую строку в файл Package. appxmanifest проекта.You can use the package manifest designer to add it by selecting Remote System on the Capabilities tab, or you can manually add the following line to your project's Package.appxmanifest file.

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

Включение обнаружения между пользователями на устройствеEnable cross-user discovering on the device

Функция удаленных сеансов ориентирована на подключение нескольких разных пользователей, поэтому на участвующих в сеансе устройствах должна быть включена функция общего доступа между пользователями.Remote Sessions is geared toward connecting multiple different users, so the devices involved will need to have Cross-User Sharing enabled. Это параметр системы, который можно запросить с помощью статического метода в классе RemoteSystem:This is a system setting that can be queried with a static method in the RemoteSystem class:

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".
}

Чтобы изменить этот параметр, пользователь должен открыть приложение Параметры.To change this setting, the user must open the Settings app. В меню общее число систем > общего > доступа в разных устройствах имеется раскрывающийся список, в котором пользователь может указать, к каким устройствам их система может предоставлять общий доступ.In the System > Shared experiences > Share across devices menu, there is a drop-down box where the user can specify which devices their system can share with.

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

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

Для использования всех фрагментов кода в этом руководстве вам потребуются следующие операторы using в файлах классов.In order to use all of the code snippets in this guide, you will need the following using statements in your class file(s).

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

Создание удаленного сеансаCreate a remote session

Для создания экземпляра удаленного сеанса необходимо начать с объекта RemoteSystemSessionController.To create a remote session instance, you must start with a RemoteSystemSessionController object. Используйте следующую платформу для создания нового сеанса и обработки запросов на присоединение от других устройств.Use the following framework to create a new session and handle join requests from other devices.

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

Создание удаленного сеанса с доступом только по приглашениюMake a remote session invite-only

Если требуется исключить общедоступное обнаружение удаленного сеанса, его можно сделать доступным только по приглашению.If you wish to keep your remote session from being publicly discoverable, you can make it invite-only. Только устройства, получившие приглашение, смогут отправлять запросы на присоединение.Only the devices that receive an invitation will be able to send join requests.

Эта процедура практически идентична указанной выше, но при создании экземпляра RemoteSystemSessionController следует передать настроенный объект RemoteSystemSessionOptions.The procedure is mostly the same as above, but when constructing the RemoteSystemSessionController instance, you will pass in a configured RemoteSystemSessionOptions object.

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

//...

Для отправки приглашения необходима ссылка на принимающую удаленную систему (получается путем обычного обнаружения удаленной системы).To send an invitation, you must have a reference to the receiving remote system (acquired through normal remote system discovery). Передайте эту ссылку методу SendInvitationAsync объекта сеанса.Simply pass this reference into the session object's SendInvitationAsync method. Все участники сеанса получают ссылку на удаленный сеанс (см. следующий раздел), поэтому любой участник может отправить приглашение.All of the participants in a session have a reference to the remote session (see next section), so any participant can send an invitation.

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

Обнаружение удаленного сеанса и присоединение к немуDiscover and join a remote session

Процесс обнаружения удаленных сеансов обрабатывается классом RemoteSystemSessionWatcher и аналогичен обнаружению отдельных удаленных систем.The process of discovering remote sessions is handled by the RemoteSystemSessionWatcher class and is similar to discovering individual remote systems.

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 его можно использовать, чтобы отправить устройству, управляющему соответствующим сеансом, запрос на присоединение.When a RemoteSystemSessionInfo instance is obtained, it can be used to issue a join request to the device that controls the corresponding session. Принятый запрос на присоединение асинхронно вернет объект RemoteSystemSessionJoinResult, содержащий ссылку на соответствующий сеанс.An accepted join request will asynchronously return a RemoteSystemSessionJoinResult object that contains a reference to the joined session.

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

Устройство может присоединиться к нескольким сеансам одновременно.A device can be joined to multiple sessions at the same time. По этой причине следует отделить функции присоединения от фактического взаимодействия с каждым сеансом.For this reason, it may be desirable to separate the joining functionality from the actual interaction with each session. Если в приложении есть ссылка на экземпляр RemoteSystemSession, через этот сеанс можно обмениваться данными.As long as a reference to the RemoteSystemSession instance is maintained in the app, communication can be attempted over that session.

Обмен сообщениями и данными через удаленный сеансShare messages and data through a remote session

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

Обмениваться сообщениями и данными с другими участвующими в сеансе устройствами можно с помощью экземпляра RemoteSystemSessionMessageChannel, который представляет единый канал обмена данными для всего сеанса.You can exchange messages and data with other participant devices in the session by using a RemoteSystemSessionMessageChannel instance, which represents a single session-wide communication channel. Сразу после инициализации он начинает прослушивать входящие сообщения.As soon as it's initialized, it begins listening for incoming messages.

Примечание

При отправке и получении сообщения необходимо сериализовывать и десериализовывать из массивов байтов.Messages must be serialized and deserialized from byte arrays upon sending and receiving. Эти возможности включены в следующие примеры, но могут быть реализованы отдельно для дополнительной модульности кода.This functionality is included in the following examples, but it can be implemented separately for better code modularity. Пример такой реализации см. в примере приложения).See the sample app) for an example of this.

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

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

После установки канала можно с легкостью отправлять сообщения всем участникам сеанса.When the channel is established, sending a message to all of the session participants is straightforward.

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

Для отправки сообщения только определенным участникам необходимо инициализировать процесс обнаружения, чтобы получить ссылки на участвующие в сеансе удаленные системы.In order to send a message to only certain participant(s), you must first initiate a discovery process to acquire references to the remote systems participating in the session. Эта процедура аналогична процессу обнаружения удаленных систем вне сеанса.This is similar to the process of discovering remote systems outside of a session. Используйте экземпляр RemoteSystemSessionParticipantWatcher для поиска участвующих в сеансе устройств.Use a RemoteSystemSessionParticipantWatcher instance to find a session's participant devices.

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

После получения списка ссылок на участников сеанса можно отправить сообщение любому их набору.When a list of references to session participants is obtained you can send a message to any set of them.

Для отправки сообщения одному участнику (желательно, выбранному пользователем на экране) передайте ссылку методу, как показано ниже.To send a message to a single participant (ideally selected on-screen by the user), simply pass the reference into a method like the following.

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

Для отправки сообщения нескольким участникам (желательно, выбранному пользователем на экране) добавьте их в объект списка и передайте этот список методу, как показано ниже.To send a message to multiple participants (ideally selected on-screen by the user), add them to a list object and pass the list into a method like the following.

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