Connecter des appareils par le biais de sessions à distance

La fonctionnalité Sessions à distance permet à une application de se connecter à d’autres appareils par le biais d’une session, soit pour la messagerie d’application explicite, soit pour l’échange réparti de données gérées par le système, comme SpatialEntityStore pour le partage holographique entre les appareils Windows Holographiques.

Les sessions à distance peuvent être créées par n’importe quel appareil Windows, et n’importe quel appareil Windows peut demander à participer (bien que les sessions puissent avoir une visibilité sur invitation uniquement), y compris les appareils connectés par d’autres utilisateurs. Ce guide fournit un exemple de code de base pour tous les principaux scénarios qui utilisent des sessions à distance. Ce code peut être incorporé dans un projet d’application existant et modifié si nécessaire. Pour une implémentation de bout en bout, consultez l’exemple d’application Quiz Game).

Configuration préliminaire

Ajouter la fonctionnalité remoteSystem

Pour que votre application puisse lancer une application sur un appareil distant, vous devez ajouter la fonctionnalité remoteSystem dans le manifeste de votre package d’application. Vous pouvez utiliser le concepteur de manifeste de package pour l’ajouter en sélectionnant Système distant sous l’onglet Fonctionnalités , ou vous pouvez ajouter manuellement la ligne suivante au fichier Package.appxmanifest de votre projet.

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

Activer la découverte inter-utilisateurs sur l’appareil

Les sessions à distance sont conçues pour connecter plusieurs utilisateurs différents, de sorte que le partage inter-utilisateurs doit être activé sur les appareils impliqués. Il s’agit d’un paramètre système qui peut être interrogé avec une méthode statique dans la 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".
}

Pour modifier ce paramètre, l’utilisateur doit ouvrir l’application Paramètres . Dans le menu Expériencespartagées>système>Partager entre les appareils, il existe une zone de liste déroulante dans laquelle l’utilisateur peut spécifier les appareils avec lesquels son système peut partager.

page des paramètres d’expériences partagées

Inclure les espaces de noms nécessaires

Pour utiliser tous les extraits de code de ce guide, vous aurez besoin des instructions suivantes using dans vos fichiers de classe.

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

Créer une session à distance

Pour créer une session à distance instance, vous devez commencer par un objet RemoteSystemSessionController. Utilisez l’infrastructure suivante pour créer une session et gérer les demandes de jointure provenant d’autres appareils.

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

Créer une session à distance sur invitation uniquement

Si vous souhaitez empêcher votre session à distance d’être détectable publiquement, vous pouvez la rendre sur invitation uniquement. Seuls les appareils qui reçoivent une invitation pourront envoyer des demandes de jointure.

La procédure est essentiellement la même que celle ci-dessus, mais lors de la construction de l’instance RemoteSystemSessionController, vous transmettez un objet RemoteSystemSessionOptions configuré.

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

//...

Pour envoyer une invitation, vous devez disposer d’une référence au système distant de réception (acquis par le biais de la découverte du système distant normal). Transmettez simplement cette référence à la méthode SendInvitationAsync de l’objet de session. Tous les participants d’une session ont une référence à la session à distance (voir la section suivante), ce qui permet à n’importe quel participant d’envoyer une invitation.

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

Découvrir et rejoindre une session à distance

Le processus de découverte des sessions à distance est géré par la classe RemoteSystemSessionWatcher et est similaire à la découverte de systèmes distants individuels.

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

Lorsqu’un instance RemoteSystemSessionInfo est obtenu, il peut être utilisé pour émettre une demande de jointure à l’appareil qui contrôle la session correspondante. Une demande de jointure acceptée retourne de façon asynchrone un objet RemoteSystemSessionJoinResult qui contient une référence à la session jointe.

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

Un appareil peut être joint à plusieurs sessions en même temps. Pour cette raison, il peut être souhaitable de séparer la fonctionnalité de jointure de l’interaction réelle avec chaque session. Tant qu’une référence à l’instance RemoteSystemSession est conservée dans l’application, la communication peut être tentée au cours de cette session.

Partager des messages et des données via une session à distance

Recevoir des messages

Vous pouvez échanger des messages et des données avec d’autres appareils participants dans la session à l’aide d’un instance RemoteSystemSessionMessageChannel, qui représente un seul canal de communication à l’échelle de la session. Dès qu’il est initialisé, il commence à écouter les messages entrants.

Notes

Les messages doivent être sérialisés et désérialisés à partir de tableaux d’octets lors de l’envoi et de la réception. Cette fonctionnalité est incluse dans les exemples suivants, mais elle peut être implémentée séparément pour une meilleure modularité du code. Consultez l’exemple d’application) pour obtenir un exemple de ceci.

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

Envoyer des messages

Lorsque le canal est établi, l’envoi d’un message à tous les participants à la session est simple.

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

Pour envoyer un message uniquement à certains participants, vous devez d’abord lancer un processus de découverte afin d’acquérir des références aux systèmes distants participant à la session. Cela est similaire au processus de découverte des systèmes distants en dehors d’une session. Utilisez un instance RemoteSystemSessionParticipantWatcher pour rechercher les appareils participants d’une session.

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

Lorsqu’une liste de références aux participants à la session est obtenue, vous pouvez envoyer un message à n’importe quel ensemble d’entre eux.

Pour envoyer un message à un seul participant (idéalement sélectionné à l’écran par l’utilisateur), transmettez simplement la référence dans une méthode comme celle-ci.

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

Pour envoyer un message à plusieurs participants (idéalement sélectionnés à l’écran par l’utilisateur), ajoutez-les à un objet de liste et transmettez la liste dans une méthode comme celle-ci.

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