iOs, communication à  travers le réseau et C#

AdraeshAdraesh Membre
juin 2014 modifié dans API UIKit #1

Bonjour à  tous,


 


Je reviens aujourd'hui sans avoir de réel problème mais simplement besoin d'un peu d'aide sur la manière de procéder concernant ce qui va suivre.


 


En effet, nous souhaitons via le réseau local faire communiquer une application iOs avec une application développée en C#. EDIT : En fait les deux applications devront pouvoir envoyer et recevoir des messages via le réseau.


 


Pour ce faire, j'ai lu et relu ces deux tutoriels :


 


https://developer.apple.com/library/mac/documentation/Networking/Conceptual/NSNetServiceProgGuide/Introduction.html#//apple_ref/doc/uid/TP40002520-SW2


http://mobileorchard.com/tutorial-networking-and-bonjour-on-iphone/


 


J'en ai déduis certaines choses :


 


Pour le serveur :


- Premièrement il faut absolument mettre en place un serveur Socket.


- Deuxièmement, il faudra déclarer se server au service Bonjour.


 


Pour le client :


- Premièrement, il faudra déduire le service Bonjour déclaré au préalable.


- Se connecter


- Récupérer les données


 


Tel que je vois le choses, j'ai pensé à  mettre donc dans la position du serveur, mon application C# et dans la position du client mon application iOs. Je n'en suis pas sur qu'en pensez vous ?


 


Ensuite, coté C# si je garde mon cheminement, je vais donc créer un server Socket et le déclarer. Par contre, coté iOS dois-je aussi créer ce serveur Socket (comme l'annonce la documentation) ? C'est à  se moment là  que je n'arrive plus trop à  suivre.


 


Avez-vous des conseils à  me donner concernant la mise en place d'un tel systeme ? Une expérience à  partager ? Comment feriez-vous ? Peut-être une explication un peu plus précise en vue de mon projet pour savoir comment je dois m'y prendre coté iOS ?


 


Merci d'avance.


Réponses

  • AdraeshAdraesh Membre
    juin 2014 modifié #2

    Bonjour,


     


    Bon nous nous sommes donc lancé dans l'aventure et je dois dire que nous avançons plutot bien. En effet, notre application C# est capable d'ouvrir le service Bonjour et de le difuser sur le réseau local.


     


    Coté iOs, un soucis réside, même si nous arrivons effectivement à  trouver notre service puisque nous passons bien dans les méthodes



    - (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)aNetServiceBrowser


    - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing

    Par contre, une fois dans la méthode didFindService, c'est là  que ça ce complique. Nous avons choisi la première méthode de connexion à  un service via les Streams comme indiqué dans la documentation officielle (là  aussi nous nous trompons peut-être).


     


    Ci-dessous le cheminement, en commençant par la recherche du service bonjour sur le réseau local depuis notre mainController :



    @property (strong, nonatomic) TestNetServiceBrowser *netServiceBrowser;


    self.netServiceBrowser = [[TestNetServiceBrowser alloc] init];
    [self.netServiceBrowser searchForServicesOfType:@_p2pchat._udp inDomain:@local];

    Ensuite tout se passe dans la classe TestNetServiceBrowser (c'est d'ailleur peut-être là  que vient le problème), voici son header :



    #import <Foundation/Foundation.h>
    #import <sys/socket.h>
    #import <netinet/in.h>

    @interface TestNetServiceBrowser : NSNetServiceBrowser <NSNetServiceBrowserDelegate, NSStreamDelegate>
    {
    }

    @property (nonatomic, retain) NSNetService *service;

    Ensuite dans notre méthode didFindService :



    - (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
    {
    self.service = aNetService;

    NSInputStream *istream = nil;
    NSOutputStream *ostream = nil;

    [self.service getInputStream:&istream outputStream:&ostream];

    if (istream && ostream)
    {
    [istream setDelegate:self];
    [ostream setDelegate:self];

    [istream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [ostream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

    [istream open];
    [ostream open];

    /*NSLog(@%u, [istream streamStatus]);
    NSLog(@%u, [ostream streamStatus]);*/
    }
    else
    {
    NSLog(@Failed to acquire valid streams);
    }

    if (!moreComing)
    {
    [self updateUI];
    }
    }

    et la méthode delegate handleEvent :



    - (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
    {
    NSLog(@stream event %i, streamEvent);
    switch (streamEvent)
    {
    case NSStreamEventOpenCompleted:
    NSLog(@Stream opened);
    break;

    case NSStreamEventHasBytesAvailable:
    break;

    case NSStreamEventErrorOccurred:
    NSLog(@Can not connect to the host!);
    break;

    case NSStreamEventEndEncountered:
    break;

    default:
    NSLog(@Unknown event);
    }
    }

    Je tiens à  préciser qu'effectivement nous ne rentrons jamais dans la méthode delegate handleEvent puisque si je log les streamStatus ils sont systématiquement à  1 ce qui d'après la doc correspond à  l'état :

     



    NSStreamStatusOpening


    The stream is in the process of being opened for reading or for writing. For network streams, this status might include the time after the stream was opened, but while network DNS resolution is happening.


     



     


    Voilà  le fond du problème nos stream sont toujours en status 1.


     


    Si quelqu'un aurait une idée plus précise de notre soucis. Je n'exclue pas qu'il est surement et fort possible que cela vienne de notre manière de procéder.


     


    Merci encore.


  • laudemalaudema Membre
    juin 2014 modifié #3

    Bonjour,


    Elle n'utilise pas Bonjour mais la démo CocoaEcho est simple et efficace.


    Est celle qui t'a inspiré ? Car elle fonctionnait correctement chez moi, la dernière fois que je l'ai essayée.


    Sinon dans ton code je ne vois pas trop ce qui peut coincer, mais je n'utilise jamais bonjour.


    Le "Truc" c'est peut être d'utiliser des CFStream et ensuite les "caster" en NSStream. Pourquoi, je ne sais plus, je l'ai découvert "the hard way" ou parce qu'ils permettent d'initialiser une connexion TCP|UDP /IP avec nom d'hôte et port qu'on a pas avec les NSStream peut être ...



     CFReadStreamRef readStream;
        CFWriteStreamRef writeStream;
        UInt32 port = (UInt32)pyxPort;
        CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@localhost,port, &readStream, &writeStream);
        //input et output sont des variables d'instance)
        input = (__bridge NSInputStream*)readStream;
        output = (__bridge NSOutputStream*)writeStream;

  • Bonjour laudema,


     


    Oui j'ai déjà  regardé du coté du sample CocoaEcho. Effectivement j'ai aussi pensé à  utiliser les CFStream, mais sans avoir encore essayé.


     


    Par contre, nous sommes d'accord que si j'utilise les CFStream je dois spécifier le même port ouvert que celui qu'utilise mon application C# ?


  • Oui, bien sûr !


    Sinon ça risque de pas marcher ;).


  • Je test ce soir je vous tiens au courant.


     


    Merci encore !


  • AdraeshAdraesh Membre
    juin 2014 modifié #7

    Après réflexion, il faut quand même que j'explique pourquoi je crois que nous sommes obligé d'utiliser bonjour (je n'en suis pas sur). Car nous ne connaissons pas l'adresse ip que va prendre l'application C# (qui nous servira de serveur) ainsi que l'adresse ip de l'appareil iOs (qui nous servira de client).


     


    Les deux seront en effet sur le réseau local et doivent pouvoir se trouver et communiquer entre eux. C'est là , si j'ai bien compris que le service Bonjour intervient, puisque grâce à  la diffusion et à  la récupération d'un service publié via Bonjour on peut en déduire les adresses de celui qui a diffusé le service et celui qui s'y est connecté.


     


    Du coup dois-je passer par une des deux autres méthodes de connexion à  un service bonjour expliqué dans la doc ? Par hostname


     


    D'ou un autre soucis toujours dans le même code ci-dessus, si dans ma méthode didFindService je fais :



    [self.service hostname]

    Celui-ci est systématiquement nul.


     


    De plus, je pense que autant l'application C# (server) ainsi que l'application iOs (client) doivent tous les deux créer et diffuser leur service pour qu'ensuite l'un capte l'autre et ils puissent communiquer ensemble, et non pas juste créer et diffuser un service coté application C#. Peut-être que je me trompe.


     


    Encore merci d'avance.


  • zoczoc Membre

    Le client n'a pas besoin de diffuser quoi que ce soit. Bonjour n'a d'intérêt que sur le serveur pour diffuser sa présence sur le réseau local et permettre aux clients de connaitre son adresse IP et son port TCP.


     


    Après, bin c'est de la communication socket tout ce qu'il y a de plus classique, ça fait 30 ans au moins que ca n'a pas changé : Le client utilise le couple IP/PORT obtenu par bonjour pour se connecter au serveur. Lors de cette connexion, le serveur est informé automatiquement par la pile IP de l'OS de l'adresse IP et du port du client.



  • Le client n'a pas besoin de diffuser quoi que ce soit. Bonjour n'a d'intérêt que sur le serveur pour diffuser sa présence sur le réseau local et permettre aux clients de connaitre son adresse IP et son port TCP.


     


    Après, bin c'est de la communication socket tout ce qu'il y a de plus classique, ça fait 30 ans au moins que ca n'a pas changé : Le client utilise le couple IP/PORT obtenu par bonjour pour se connecter au serveur. Lors de cette connexion, le serveur est informé automatiquement par la pile IP de l'OS de l'adresse IP et du port du client.




     


    Bonjour, c'est bien ce que j'ai compris, mais ça ne règle pas mon soucis de connexion qui reste sur l'état Openning et qui ne s'ouvre jamais ...

  • Essaye de voir ce qui passe sur ton réseau avec l'application "Utilitaire de Réseau" dans le dossier Utilitaires du dossier Applications.


    Tu pourras voir si ton appli publie et sur quel port avec le dernier onglet "Port Scan".


  • Bonjour à  tous,


     


    Notre problème est résolu, nous avons simplement inversé les rôles c'est donc l'iPad qui est serveur et notre application C# qui est cliente.


     


    Du coup c'est l'application iOs qui va créer et publier le service bonjour et ensuite ouvrir les streams. L'application C# récupère le service et resolve le hostname.


     


    Il nous reste quelques petites choses à  régler mais tout à  l'air de fonctionner parfaitement.


     


    Merci encore.


  • zoczoc Membre

    Donc plutôt que d'essayer de comprendre pourquoi ça ne marchait pas, vous avez choisi la mauvaise solution...


     


    Maintenant, si vous avez 2 iPad sur le réseau qui veulent utiliser le service, il se passe quoi vu qu'ils annoncent tous les 2 la même chose (hormis leur adresse IP) ? Le "client" C# boucle sur toutes les annonces et se connecte à  chaque iPad ?

  • Bonsoir,


     


    Nous utilisons les classes EchoConnection et EchoServer d'Apple. Effectivement, la prochaine étape est de gérer les multiples connexion au réseau local sur un même service (ou plusieurs services à  voir ...).


     


    Pour ton information nous n'avons pas choisi la mauvaise solution au contraire, nous nous retrouvons bien dans ces classe Apple et nous retrouvons exactement ce que nous faisions de notre coté à  peu de choses prêt.


  • zoczoc Membre
    juin 2014 modifié #14

    Vous avez juste inversé les notions de serveur et de client. Pour moi c'est la mauvaise solution... Jusqu'à  preuve du contraire, le système qui propose un service est un serveur, le système qui l'utilise est un client.


     


    Je vous souhaite bonne chance pour gérer plusieurs clients de manière propre avec votre solution (alors que dans l'autre sens ça se fait tout seul, il y a des API pour ça).


     

    Et même sans aller si loin : Comment l'application sur l'iPad va-t-elle pouvoir détecter que le serveur (votre client du coup) n'est pas disponible et afficher un message d'avertissement à  l'utilisateur si ce n'est pas lui qui se connecte au serveur mais se contente d'attendre une connexion entrante qui n'arrivera peut-être jamais ?

     

    Bref, je m'en retourne utiliser mon "Serveur Safari" pour me connecter à  mon "Client Apache"  :p


  • AliGatorAliGator Membre, Modérateur
    Ma voiture ne flotte pas quand je la mets dans l'eau. Ah si, ça y est, elle flotte, il suffisait de la mettre sur le toit et non sur les roues pour qu'elle flotte, c'est bon, problème résolu ! Hein, quoi, troquer ma voiture pour un bateau ? Bah pour quoi faire, ma voiture flotte maintenant !
  • AdraeshAdraesh Membre
    juin 2014 modifié #16

    Bonjour,


     


    Pour être honnête le problème est assez délicat, en effet, on peut penser qu'on inverse les choses et nous sommes justement entrain de réfléchir à  la question.


     


    Il y a plusieurs choses à  prendre en considération et effectivement dans la logique des choses l'application C# devrait être le serveur et le ou les iPad(s) le(s) client(s). Dans ce cas de figure il y aurait donc un serveur et le ou les client(s).


     


    Le problème c'est qu'on peut très bien envisage (dans notre cas), plusieurs ordinateur sur le même réseau qui font tourner plusieurs version de notre application C# (donc plusieurs serveur ?!)


     


    Ce n'est pas si évident que ça de définir qui va être serveur ou client.


     


    On travaille là  dessus ...


     


    EDIT : Pour vous donner toutes les cartes en main et que vous compreniez un peu mieux ce qu'on fait, notre application C# récupère des données d'un autres soft et doit les envoyer à  un ou plusieurs iPad(s) connecté(s).


     


    Par contre on peut avoir ce soft installé sur plusieurs ordinateurs du même réseau et notre version de notre application C# installé aussi, dans ce cas il faudra gérer sur quel iPad on envoit les données, tel iPad doit communiquer avec tel application C# etc ...


Connectez-vous ou Inscrivez-vous pour répondre.