Conectar dispositivos por meio de sessões remotas

O recurso Sessões Remotas permite que um aplicativo se conecte a outros dispositivos por meio de uma sessão, seja para mensagens explícitas do aplicativo ou para troca agenciada de dados gerenciados pelo sistema, como o SpatialEntityStore para compartilhamento holográfico entre dispositivos Windows Holographic.

As sessões remotas podem ser criadas por qualquer dispositivo Windows e qualquer dispositivo Windows pode solicitar a participação (embora as sessões possam ter visibilidade somente para convidados), incluindo dispositivos conectados por outros usuários. Este guia fornece código de exemplo básico para todos os principais cenários que usam sessões remotas. Esse código pode ser incorporado a um projeto de aplicativo existente e modificado conforme necessário. Para obter uma implementação de ponta a ponta, consulte o aplicativo de exemplo Quiz Game).

Configuração preliminar

Adicionar a funcionalidade remoteSystem

Para que seu aplicativo inicie um aplicativo em um dispositivo remoto, você deve adicionar a funcionalidade ao manifesto remoteSystem do pacote do aplicativo. Você pode usar o designer de manifesto do pacote para adicioná-lo selecionando Sistema Remoto na guia Recursos ou pode adicionar manualmente a linha a seguir ao arquivo Package.appxmanifest do projeto.

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

Habilitar a descoberta entre usuários no dispositivo

As Sessões Remotas são voltadas para conectar vários usuários diferentes, portanto, os dispositivos envolvidos precisarão ter o Compartilhamento entre Usuários ativado. Esta é uma configuração do sistema que pode ser consultada com um método estático na classe 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".
}

Para alterar essa configuração, o usuário deve abrir o aplicativo Configurações . No menu Experiências>compartilhadas do sistema>Compartilhar entre dispositivos, há uma caixa suspensa em que o usuário pode especificar com quais dispositivos seu sistema pode compartilhar.

Página de configurações de experiências compartilhadas

Inclua os namespaces necessários

Para usar todos os snippets de código neste guia, você precisará das seguintes using instruções em seus arquivos de classe.

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

Criar uma sessão remota

Para criar uma instância de sessão remota, você deve começar com um objeto RemoteSystemSessionController. Use a estrutura a seguir para criar uma nova sessão e lidar com solicitações de junção de outros dispositivos.

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

Tornar uma sessão remota somente para convidados

Se você deseja impedir que sua sessão remota seja descoberta publicamente, você pode torná-la somente para convidados. Somente os dispositivos que receberem um convite poderão enviar solicitações de participação.

O procedimento é basicamente o mesmo acima, mas ao construir a instância RemoteSystemSessionController, você passará um objeto RemoteSystemSessionOptions configurado.

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

//...

Para enviar um convite, você deve ter uma referência ao sistema remoto receptor (adquirida por meio da descoberta normal do sistema remoto). Basta passar essa referência para o método SendInvitationAsync do objeto de sessão. Todos os participantes de uma sessão têm uma referência à sessão remota (consulte a próxima seção), portanto, qualquer participante pode enviar um convite.

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

Descobrir e ingressar em uma sessão remota

O processo de descoberta de sessões remotas é tratado pela classe RemoteSystemSessionWatcher e é semelhante à descoberta de sistemas remotos individuais.

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

Quando uma instância RemoteSystemSessionInfo é obtida, ela pode ser usada para emitir uma solicitação de junção para o dispositivo que controla a sessão correspondente. Uma solicitação de junção aceita retornará de forma assíncrona um objeto RemoteSystemSessionJoinResult que contém uma referência à sessão unida.

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

Um dispositivo pode ser ingressado em várias sessões ao mesmo tempo. Por esse motivo, pode ser desejável separar a funcionalidade de junção da interação real com cada sessão. Desde que uma referência à instância RemoteSystemSession seja mantida no aplicativo, a comunicação pode ser tentada nessa sessão.

Compartilhe mensagens e dados por meio de uma sessão remota

Receber mensagens

Você pode trocar mensagens e dados com outros dispositivos participantes na sessão usando uma instância RemoteSystemSessionMessageChannel , que representa um único canal de comunicação em toda a sessão. Assim que é inicializado, ele começa a ouvir as mensagens recebidas.

Observação

As mensagens devem ser serializadas e desserializadas de matrizes de bytes ao enviar e receber. Essa funcionalidade está incluída nos exemplos a seguir, mas pode ser implementada separadamente para melhorar a modularidade do código. Consulte o aplicativo de exemplo) para obter um exemplo disso.

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

Enviar mensagens

Quando o canal é estabelecido, enviar uma mensagem para todos os participantes da sessão é simples.

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

Para enviar uma mensagem apenas para determinados participantes, você deve primeiro iniciar um processo de descoberta para adquirir referências aos sistemas remotos que participam da sessão. Isso é semelhante ao processo de descoberta de sistemas remotos fora de uma sessão. Use uma instância RemoteSystemSessionParticipantWatcher para localizar os dispositivos participantes de uma sessão.

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

Quando uma lista de referências aos participantes da sessão é obtida, você pode enviar uma mensagem para qualquer conjunto deles.

Para enviar uma mensagem a um único participante (idealmente selecionado na tela pelo usuário), basta passar a referência para um método como o seguinte.

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

Para enviar uma mensagem a vários participantes (idealmente selecionados na tela pelo usuário), adicione-os a um objeto de lista e passe a lista para um método como o seguinte.

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