- Please find the en-US version here -

Avec WinRT, il est possible de partager des données entre 2 machines sous Windows 8.1 qui exécutent la même application. Vous pouvez ainsi faire communiquer des machines qui se trouvent à proximité, sans connexion internet ! Pour cela, il vous suffit d’avoir des équipements qui supportent la technologie Wifi-Direct : c’est le cas de la Surface RT ainsi que de la majorité des machines actuelles. Ce scénario est complémentaire au partage via le charm/talisman qui vous permet de partager du contenu entre 2 applications différentes sur une même machine.

Le Wifi-Direct est une alternative intéressante à la technologie NFC pour certains scenarios : pas besoin de puce spécifique pour faire de la communication de proximité. Si vous souhaitez tester le NFC sous Windows 8 sans néanmoins disposer du hardware, vous trouverez ici comment installer un émulateur : Windows 8.1 : Utiliser la Near-Field Proximity API sans matériel NFC.

La communication en Wifi-Direct se fait à travers l’API Proximity de WinRT (c’est aussi le cas pour NFC mais avec des scenarios un peu différents).

Je vous propose une petite lib qui vous permet de faire très facilement du transfert de fichier en Wifi-Direct, et une sample app pour la tester.

Attention, je fournis cette solution à des fins de démonstration, donc en l’état, pour une utilisation en production, c’est à vos risques et périls.

Dans cet article, je détaillerai:

- comment utiliser la Library et l’application de démo qui la met en œuvre

- comment fonctionne l’API Proximity sur laquelle repose la lib pour le Wifi-Direct

- des ouvertures vers d’autres scenarios

Comment fonctionne la librairie ?

La lib WifiDirectTransfer repose sur l’API Proximity de WinRT. Elle est très simple à utiliser : 3 lignes de code suffisent.

Elle contient une classe WifiDirectFileTransfer.

image_thumb2

C’est la même classe qui est utilisée par l’émetteur et le récepteur.

public WifiDirectFileTransfer(Action<string> verboseCb = null)
public void Start()
public async Task<IEnumerable<PeerInformation>> FindPeersAsync()
public async Task ConnectAndSendFileAsync(PeerInformation selectedPeer, StorageFile selectedFile)
public async Task<string> ReceiveFileAsync(PeerInformation requestingPeer, 
StorageFolder folder, string outputFilename = null)

Vous avez la possibilité de passer un delegate au constructeur: il sera appelé au fur et à mesure du processus de communication, avec un message d’information indiquant l’étape en cours.

Après avoir instancié la classe, vous pourrez:

Emetteur

- Rechercher d’autres machines exécutant la même application (Start)

- Envoyer un fichier à une de ces machines (ConnectAndSendFileAsync)

Récepteur

- Vous abonner à l’évènement ConnectionRequested

- Attendre des demandes de connexion de la part d’autres machines exécutant la même application (Start)

- Accepter une demande de connexion, recevoir un fichier et le sauvegarder sur le disque (ReceiveFileAsync).

Mettre à jour le manifest

Les applications qui utilisent cette lib devront cocher la capacité Proximity du manifest:

image_thumb[1]

Tester l’application de démo fournie avec la solution

Cette application doit être lancée sur (au moins) 2 machines sous Windows 8.1 supportant le Wifi-Direct.

Emetteur          
image12_thumb[4]
Récepteur        
image15_thumb[4]

Sur la machine depuis laquelle vous souhaitez partager un fichier, cliquez sur le premier bouton.

Emetteur          
image5_thumb[4]
Récepteur        
image21_thumb[6]

Elle vous affiche la liste des autres machines qui exécutent la même application à proximité, en l’occurrence ici 1 machine nommée SurfaceRT.

Double-cliquez sur la machine de la liste à laquelle vous souhaitez vous connecter.

Emetteur

image_thumb27

Sélectionnez une image à transférer et cliquez sur le bouton Ouvrir, en bas à droite de l’écran.

Récepteur

image_thumb14

La récepteur SurfaceRT vient de détecter la demande de connexion provenant de la machine nommée MININT-A34GQT7. Cliquez sur le 2ème bouton qui vient de s’activer. La phase de connexion est un peu longue (autour de 10 secondes sur ma machine), un peu de patience Sourire.

Une fois la connexion établie dans les 2 sens, le transfert de fichier va commencer.

Emetteur          
image_thumb1[1]
Récepteur       
image30_thumb[4]

La progression du téléchargement va défiler sur la barre de statut.

Puis, l’image sera sauvegardée sur le disque et affichée à l’écran : TADA !

Récepteur

image_thumb18

NB : Pour un nouveau transfert, redémarrez l’application.

Comment fonctionne l’API Proximity de WinRT pour Wifi-Direct

La lib se base sur l’API proximity qui fournit un mécanisme simple pour communiquer en à travers un socket TCP. Voici les principales étapes:

Chaque machine démarre le processus en appelant

PeerFinder.Start()

Vous pouvez ensuite rechercher d’autres machines exécutant la même application avec

PeerFinder.FindAllPeersAsync()

Vous pouvez également attribuer des rôles (host, client, peer) à chacun pour ne détecter que ceux qui vous intéressent.

En vous connectant ensuite à l’une des machines trouvées, vous obtiendrez un socket.

var socket = await PeerFinder.ConnectAsync(selectedPeer);

La machine cible sera prévenue de votre demande de connexion grâce au déclenchement d’un évènement auquel elle s’est abonnée au préalable:

PeerFinder.ConnectionRequested += PeerFinder_ConnectionRequested;

Elle peut ensuite se connecter en retour:

StreamSocket socket = await PeerFinder.ConnectAsync(requestingPeer);

Les deux partis peuvent maintenant communiquer à travers le socket.

Et si je veux transférer autre chose qu’un fichier ?

Le support de communication étant un socket, vous pouvez transférer tout ce qui y transite habituellement c’est à dire à peu près n’importe quoi Sourire. Vous devez simplement faire en sorte que l’émetteur et le récepteur se comprennent.

Dans mon exemple, pour un fichier, j’ai mis en place un petit protocole en 4 étapes:

  • émission de la taille du nom du fichier
  • émission du nom du fichier
  • émission de la taille du fichier
  • émission du fichier lui-même

Voici le bout de code correspondant pour l’émetteur:

private async Task SendFileToPeerAsync(PeerInformation selectedPeer, StreamSocket socket, StorageFile selectedFile)      
{
    byte[] buff = new byte[BLOCK_SIZE];
    var prop = await selectedFile.GetBasicPropertiesAsync();
    using (var dw = new DataWriter(socket.OutputStream))
    {

        // 1. Send the filename length
        dw.WriteInt32(selectedFile.Name.Length); // filename length is fixed
        // 2. Send the filename
        dw.WriteString(selectedFile.Name);
        // 3. Send the file length
        dw.WriteUInt64(prop.Size);
        // 4. Send the file
        var fileStream = await selectedFile.OpenStreamForReadAsync();
        while (fileStream.Position < (long)prop.Size)
        {
            var rlen = await fileStream.ReadAsync(buff, 0, buff.Length);
            dw.WriteBytes(buff);
        }

        await dw.FlushAsync();
        await dw.StoreAsync();

        await socket.OutputStream.FlushAsync();
    }
}

Le récepteur lira les données dans le même ordre:

private async Task<string> ReceiveFileFomPeer(StreamSocket socket, StorageFolder folder, string outputFilename = null)
{
    StorageFile file; 
    using (var rw = new DataReader(socket.InputStream))
    {
        // 1. Read the filename length
        await rw.LoadAsync(sizeof(Int32));
        var filenameLength = (uint)rw.ReadInt32();
        // 2. Read the filename
        await rw.LoadAsync(filenameLength);
        var originalFilename = rw.ReadString(filenameLength);
        if (outputFilename == null)
        {
            outputFilename = originalFilename;
        }
        //3. Read the file length
        await rw.LoadAsync(sizeof(UInt64));
        var fileLength = rw.ReadUInt64();

        // 4. Reading file
        using (var memStream = await DownloadFile(rw, fileLength))
        {
            file = await folder.CreateFileAsync(outputFilename, CreationCollisionOption.ReplaceExisting);
            using (var fileStream1 = await file.OpenAsync(FileAccessMode.ReadWrite))
            {
                await RandomAccessStream.CopyAndCloseAsync(memStream.GetInputStreamAt(0), fileStream1.GetOutputStreamAt(0));
            }
            Verbose("Et voila :)");

            rw.DetachStream();
        }
    }
    return file.Path;
}

D’autres scenarios avec l’API Proximity

D’autres possibilités s’offrent à vous, comme s’abonner à tout évènement de proximité (grâce à PeerFinder.CreateWatcher) ou fournir des informations en avance de phase lors de la connexion. Par-exemple, on pourrait décider si oui ou non on accepte la connexion en fonction de ces informations.