La présentation est en train de télécharger. S'il vous plaît, attendez

La présentation est en train de télécharger. S'il vous plaît, attendez

Android_Clients_Serveur 1 Connectivité, Web Services Orienté clients/serveur TCP et Bluetooth Notes de cours jean-michel Douin, douin au cnam point fr.

Présentations similaires


Présentation au sujet: "Android_Clients_Serveur 1 Connectivité, Web Services Orienté clients/serveur TCP et Bluetooth Notes de cours jean-michel Douin, douin au cnam point fr."— Transcription de la présentation:

1 Android_Clients_Serveur 1 Connectivité, Web Services Orienté clients/serveur TCP et Bluetooth Notes de cours jean-michel Douin, douin au cnam point fr version : 26 Mars 2013 Avertissement :

2 Android_Clients_Serveur 2 Bibliographie utilisée Un ensemble de tutoriels à lire

3 Android_Clients_Serveur 3 Sommaire Clients et serveurs TCP –Un Client, une requête –Serveur TCP (HTTP,…) –Clients Bluetooth Annexes, cf. NFP121 –Format XML, API SAX –Format JSON json.org

4 Android_Clients_Serveur 4 Client et serveur Serveurs TCP –Exemples –Requête

5 Android_Clients_Serveur 5 Serveurs En mode TCP Appels distants en mode TCP/IP –Point à point avec accusé de réception –telnet, ftp, http, …

6 Android_Clients_Serveur 6 URL … URL Uniform Resource Locator une adresse sur internet –http://jfod.cnam.fr –http le protocole –//jfod.cnam.fr le nom de la ressource –http://jfod.cnam.fr:8999/ds2438/mesures.html

7 Android_Clients_Serveur 7 Exemples clients / serveurs 1.Le client sadresse au serveur –Établit une connexion 2.Le serveur satisfait ses clients –Mode synchrone, analogue à lappel dune méthode locale Client2 Serveur Client1 protocole Client3

8 Android_Clients_Serveur 8 Appels distants protocole « maison » Le contexte –Client Java, ou autres –Serveur en java ou autre –maison : //serveur/…. Client2 serveur JVM Client1 JVM maison Client3

9 Android_Clients_Serveur 9 Appels distants protocole http Le contexte –Client Java(application comme applette), ou autres –Un navigateur –Serveur en java, ou autres http: //serveur/index.html Standard, universel … Client2 serveur JVM Client1 JVM http un navigateur

10 Android_Clients_Serveur 10 Implémentation en Java Paquetage java.net –Principales classes ServerSocket Socket InetAddress URLConnection … –Quelques lignes de sources suffisent …

11 Android_Clients_Serveur 11 usage de java.net TCP/IP 2 classes essentielles Côté Serveur –java.net.ServerSocket Méthode accept() sur une instance de la classe ServerSocket Côté Client –java.net.Socket, java.net.SocketAddress Envoi sur une instance de la classe Socket de données Serveur JVM Client1 JVM

12 Android_Clients_Serveur 12 Connexion / Principes Le Serveur attend une requête sur son port –ServerSocket server = new ServerSocket(port) –Socket socket = server.accept(); Dès la connexion établie, –une instance de la classe Socket est engendrée sur un port temporaire Établir une connexion par le client est effectuée par –Socket s = new Socket(Serveur, port) Serveur JVM Client1 JVM port

13 Android_Clients_Serveur 13 2 exemples Serveur et client 1.Au protocole « maison » Le serveur ne connaît que la commande « parle » et répond « bonjour » Tout autre commande est ignorée ! 2.Au protocole http Seule la méthode GET /index.html HTTP1.0 est possible Un sous-ensemble donc …

14 Android_Clients_Serveur 14 Exemple 1 –Au protocole « maison » Le serveur ne connaît que la commande « parle » et répond « bonjour » Tout autre commande est ignorée ! –Client java ou autre Serveur JVM Client1 JVM maison: //serveur parle bonjour

15 Android_Clients_Serveur 15 Un serveur avec un protocole « maison » public class Serveur{ public static void main(String[] args) throws Exception{ ServerSocket serveur = new ServerSocket(5000); while(true) { Socket socket = serveur.accept(); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); String cmd = in.readLine(); // parle !!! DataOutputStream out = new DataOutputStream( socket.getOutputStream()); if(cmd.equals("parle")){ out.write("bonjour\n".getBytes()); }else{ out.write("commande inconnue ?\n".getBytes()); } socket.close(); }

16 Android_Clients_Serveur 16 Le client « maison » public class Client{ public static void main(String[] args) throws Exception{ Socket socket = new Socket("vivaldi.cnam.fr", 5000); DataOutputStream out= new DataOutputStream( socket.getOutputStream()); out.write(args[0].getBytes()); out.write("\n".getBytes()); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); System.out.println(in.readLine()); socket.close(); }

17 Android_Clients_Serveur 17 Un client « maison », telnet telnet localhost 5000 –parle // frappe sans écho petit outil utile : tcpview

18 Android_Clients_Serveur 18 Exemple 2 Le protocole HTTP –Les méthodes GET, POST, …. Mise en œuvre / démo –Usage dun client telnet sur un site existant –Une application Java cliente –Un serveur en java

19 Android_Clients_Serveur 19 Protocole HTTP HyperText Transfer Protocol –Au dessus de TCP Les Méthodes –GET /index.html HTTP/1.0 –HEAD –POST –PUT –DELETE –TRACE –CONNECT –Voir

20 Android_Clients_Serveur 20 Côté serveur, méthode accept ServerSocket listen = new ServerSocket(HTTP_PORT); while(!stopped()){ try{ Socket socket = listen.accept(); handleRequest(socket); }catch(Exception e){ } listen.close(); } listen.accept() est bloquant : au moins un client.

21 Android_Clients_Serveur 21 Exemple minimaliste ! Un serveur Web au complet –Un seul client, une seule requête ! Extrait de

22 Android_Clients_Serveur 22 OneShot Httpd by Hendrik, j2se public class OneShotHttpd { protected static File docRoot; public final static int HTTP_PORT = 8080; public static void main(String argv[]){ try{ docRoot = new File("."); ServerSocket listen = new ServerSocket(HTTP_PORT); Socket client = listen.accept(); BufferedReader is = new BufferedReader(new InputStreamReader(client.getInputStream())); DataOutputStream os = new DataOutputStream(client.getOutputStream()); String request = is.readLine(); StringTokenizer st = new StringTokenizer(request); if((st.countTokens() == 3) && st.nextToken().equals("GET")){ String filename = docRoot.getPath() + st.nextToken(); if(filename.endsWith("/") || filename.equals("")) filename += "index.html"; File file = new File(filename); sendDocument(os,file); } else System.err.println("400 Bad Request"); is.close(); os.close(); client.close(); }catch(IOException ioe){ System.err.println("Error: " + ioe.toString()); }}

23 Android_Clients_Serveur 23 OneShot « envoi du document » public static void sendDocument(DataOutputStream out, File file) throws IOException{ try{ BufferedInputStream in = new BufferedInputStream( new FileInputStream(file)); byte[] buf = new byte[1024]; int len; while((len = in.read(buf,0,1024)) != -1) { out.write(buf,0,len); } in.close(); } catch(FileNotFoundException fnfe) { System.err.println("404 Not Found"); }

24 Android_Clients_Serveur 24 OneShot avec Android AsyncTask Rappel –Réalise une encapsulation dun Thread et de laccès à lécran onPreExecute() –Préambule, lUI exécute cette méthode Void doInBackground(String… s){ } onProgressUpdate(Progress…p) –Mise à jour de lUI à la suite de lappel de publishProgress onPostExecute(Result) –Mise à jour de lUI à la fin de la méthode doInBackground OneShotHttpd.main(s);

25 Android_Clients_Serveur 25 Côté serveur, un thread à chaque requête ServerSocket listen = new ServerSocket(HTTP_PORT); while(!stopped()){ try{ new Connection(listen.accept()); // création dune instance }catch(Exception e){ } listen.close(); } Chaque requête engendre la création dune instance de la classe Connection Et chaque instance créée engendre à son tour un « Thread »

26 Android_Clients_Serveur 26 Côté serveur, à chaque Connection un Thread public class Connexion extends Thread{ … public Connexion(Socket s){ this.s = s; start(); } public void run(){ try{ BufferedReader is = new BufferedReader( new InputStreamReader(s.getInputStream())); DataOutputStream os = new DataOutputStream(s.getOutputStream()); // analyse du contenu au bon protocole HTTP // envoi du document

27 Android_Clients_Serveur 27 Côté serveur, accept « peut-être » ServerSocket listen = new ServerSocket(HTTP_PORT); listen.setSoTimeout(TIME_OUT); while(!stopped()){ try{ new Connection(listen.accept()); }catch(SocketTimeoutException e){ // ici délai de garde échu }catch(Exception e){ } listen.close(); } Par défaut lappel de accept est bloquant Méthode accept avec délai de garde exception SocketTimeoutException à léchéance

28 Android_Clients_Serveur 28 Schéma avec Un Pool de Thread class WebServer { // 2004 JavaOneSM Conference | Session 1358 ThreadPool pool = new ThreadPool(7); public static void main(String[] args) { ServerSocket socket = new ServerSocket(80); while (true) { final Socket s = socket.accept(); Runnable r = new Runnable() { public void run() { BufferedReader is = new BufferedReader( new InputStreamReader(s.getInputStream())); DataOutputStream os = new DataOutputStream(s.getOutputStream()); // analyse du contenu au bon protocole HTTP // envoi du document } }; pool.execute(r); } }}

29 Android_Clients_Serveur 29 Côté client Usage de telnet Requêtes GET et POST en Java

30 Android_Clients_Serveur 30 Requête GET avec telnet Un client telnet et un site du Cnam –telnet jfod.cnam.fr 80 GET /index.html HTTP/1.0 ( frappe sans écho) HTTP/ OK Last-Modified: Thu, 08 Feb :55:29 GMT Date: Thu, 08 Mar :33:55 GMT Server: Brazil/1.0 Content-Length: 7624 Content-Type: text/html Connection: close ….. Le résultat est retourné, le source du fichier index.html précédé de quelques informations

31 Android_Clients_Serveur 31 Requête GET en Java Lessentiel –Créer une URL –Ouvrir une connexion Écrire et lire sur les flots associés Classe java.net.URL Classe java.net.URLConnection – URL url = new URL("http://jfod.cnam.fr/index.html" ); – URLConnection connection = url.openConnection();

32 Android_Clients_Serveur 32 Requête GET au complet public void testGET()throws Exception{ URL url = new URL("http://jfod.cnam.fr/index.html" ); URLConnection connection = url.openConnection(); BufferedReader in = new BufferedReader( new InputStreamReader(connection.getInputStream())); String inputLine = in.readLine(); while(inputLine != null){ System.out.println(inputLine); inputLine = in.readLine(); } in.close(); }

33 Android_Clients_Serveur 33 Requête GET avec paramètres public void testGET()throws Exception{ URL url = new URL("http://jfod.cnam.fr:8999/ds2438/ ?listAll=on " ); URLConnection connection = url.openConnection(); connection.setDoInput(true); BufferedReader in = new BufferedReader( new InputStreamReader(connection.getInputStream())); String inputLine = in.readLine(); while(inputLine != null){ System.out.println(inputLine); inputLine = in.readLine(); } in.close(); }

34 Android_Clients_Serveur 34 Requête POST URL url = new URL ("http://jfod.cnam.fr/index.html" ); URLConnection connection = url.openConnection(); connection.setDoInput(true); connection.setDoOutput(true); PrintWriter out = new PrintWriter(connection.getOutputStream()); out.print("listAll=on"); out.close(); BufferedReader in = new BufferedReader( new InputStreamReader(connection.getInputStream())); String inputLine = in.readLine(); while(inputLine != null){ System.out.println(inputLine); inputLine = in.readLine(); } in.close();

35 Android_Clients_Serveur 35 Classes utiles InetAddress –Adresse IP en « clair » URL –Pour Uniform Resource Locator, sur le www URLConnection –Une classe abstraite, super classe de toutes les classes établissant un lien entre une application et une URL –Sous-classes HttpURLConnexion, JarURLConnection –Patron Fabrique afin décrire son propre gestionnaire de protocole Voir Méthode URLConnection.setContentHandlerFactory( …);

36 Android_Clients_Serveur 36 En résumé Classe daccès aux informations –indépendante du protocole choisi Lecture écriture en 7 étapes 1.Après avoir créé l URL. 2.Obtenir linstance URLConnection. 3.Installer les capacités en sortie de cette instance de URLConnection. 4.Ouvrir le flot en entrée. 5.Obtenir le flot en sortie. 6.Écrire sur ce flot. 7.Fermer celui-ci.

37 Android_Clients_Serveur 37 Android –Toute requête doit être effectuée en dehors de lUIThread: –En conséquence, usage de AsyncTask Service + Thread

38 Android_Clients_Serveur 38 En « rappel » le cycle de vie

39 Android_Clients_Serveur 39 En Rappel: AsyncTask Avec la classe, AsyncTask –http://developer.android.com/reference/android/os/AsyncTask.htmlhttp://developer.android.com/reference/android/os/AsyncTask.html Nous avons –Un thread et laccès à lUIThread Un thread : pour le traitement en tâche de fond Une mise à jour de lUI incluse Les paramètres génériques sont –Params type des paramètres transmis au Thread –Progress type des paramètres en cours de traitement transmis au Handler –Result type du résultat pour lappelant

40 Android_Clients_Serveur 40 Résumé: AsyncTask Depuis lUIThread –création dune instance et appel de la méthode execute Exemple new WebAsyncTask().execute(url1, url2, url3); AsyncTask –Réalise une encapsulation dun Thread et de laccès à lécran Méthodes onPreExecute() –Préambule, lUI exécute cette méthode Result doInBackground(Params…p) –Le contenu de cette méthode sexécute dans un autre Thread onProgressUpdate(Progress…p) –Mise à jour de lUI à la suite de lappel de publishProgress onPostExecute(Result) –Mise à jour de lUI à la fin de la méthode doInBackground

41 Android_Clients_Serveur 41 AsyncTask et réseau, exemples Lire une page sur le web HTTP, requête GET –private class LirePageHTML extends AsyncTask { Schéma onPreExecute Afficher une fenêtre dinformations, ProgressDialog doInBackGround Ouvrir une connexion, avec un échec éventuel onPostExecute Informer lutilisateur

42 Android_Clients_Serveur 42 Lire une page Web Si jai la permission … de naviguer sur le web – –Une IHM simple –Laccès au web est une opération coûteuse alors héritons de AsyncTask

43 Android_Clients_Serveur 43 Une classe interne héritant de AsyncTask protected String doInBackground(String... args) { builder = new StringBuilder(); try { HttpClient client = new DefaultHttpClient(); HttpGet httpGet = new HttpGet(args[0]); HttpResponse response = client.execute(httpGet); StatusLine statusLine = response.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode == 200) { HttpEntity entity = response.getEntity(); InputStream content = entity.getContent(); BufferedReader reader = new BufferedReader( new InputStreamReader(content)); String line; while ((line = reader.readLine()) != null) { builder.append(line); } } else {error = "Failed to download file";} } catch (Exception e) {error = e.getMessage();} return builder.toString();}

44 Android_Clients_Serveur 44 Autres exemples, essai darchitecture 1) Ouverture dune connexion TCP –Obtention dun flux (OutputStream) –Le flux reste ouvert 2) Envois de données sur le flux –En fonction des opérations de lutilisateur Règle : louverture de la connexion et lenvoi de données se font sur des threads Une solution : –Ouverture dune connexion TCP : dans une sous classe dAsyncTask –Envoi de données : dans un thread en attente sur une file (SynchronousQueue) –java.util.concurrent.SynchronousQueue

45 Android_Clients_Serveur 45 Un schéma dune architecture possible UIThread AsyncTask Un Thread Réseau SynchronousQueue offer take 2) Envoi de données 1) Obtention de la connexion

46 Android_Clients_Serveur 46 Obtention de la connexion, AsyncTask protected void onPreExecute() { dialog = ….); } protected DataOutputStream doInBackground(String... args) { boolean result = true; try{ InetAddress addr = InetAddress.getByName(args[0]); int port = Integer.parseInt(args[1]); int timeout = Integer.parseInt(args[2]); SocketAddress sockaddr = new InetSocketAddress(addr, port); this.socket = new Socket(); socket.connect(sockaddr, timeout); out= new DataOutputStream(socket.getOutputStream()); }catch(Exception e){ erreur = e.getMessage();result= false;} return out; }

47 Android_Clients_Serveur 47 Envoi de données depuis lUIThread // ici à chaque clic des données sont envoyées vers la file // les boutons de lIHM contiennent la commande à envoyer au serveur public void onClickCommand(View v){ String cmd = v.getContentDescription().toString() + "\n"; try { sender.offer(cmd.getBytes()); } catch (Exception e) { } UIThread offer

48 Android_Clients_Serveur 48 Envois de données, vers la file public class Sender extends Thread{ private BlockingQueue queue; public Sender(){ queue = new SynchronousQueue (); this.start(); } public boolean offer(byte[] cmd){ return queue.offer(cmd); } public void close(){ this.interrupt(); } public void run(){ while(!isInterrupted()){ try { byte[] cmd = queue.take(); // lecture bloquante out.write(cmd); }catch (Exception e) { }} }

49 Android_Clients_Serveur 49 Une connexion Bluetooth Recherche dun périphérique bluetooth aux alentours –Hypothèse : Nous connaissons ladresse physique du périphérique EF C (obtenu ipconfig /all sous windows) –Cette recherche doit seffectuer dans un thread Alors héritons de AsyncTask Au clic –new ConnexionBT().execute("00:19:EF:01:17:9C"); private class ConnexionBT extends AsyncTask { protected void onPreExecute() { protected BluetoothSocket doInBackground(String... args) { protected void onPostExecute(BluetoothSocket btSocket) {

50 Android_Clients_Serveur 50 onPreExecute : Patience, doInBackground : Recherche protected void onPreExecute() { dialog = ProgressDialog.show(BTClientActivity.this, "connexion Bluetooth", " patientez ", true); } protected BluetoothSocket doInBackground(String... args) { try{ this.btDevice = btAdapter.getRemoteDevice(args[0]); btSocket = btDevice.createRfcommSocketToServiceRecord(MY_UUID); btAdapter.cancelDiscovery(); btSocket.connect(); }catch(Exception e){ erreur = e.getMessage(); btSocket= null; } return btSocket; }

51 Android_Clients_Serveur 51 onPostExecute(BluetoothSocket btSocket) protected void onPostExecute(BluetoothSocket btSocket) { try { os = btSocket.getOutputStream(); // } catch (IOException e) { erreur = e.getMessage(); e.printStackTrace(); }finally{ dialog.dismiss(); } }

52 Android_Clients_Serveur 52 Première conclusion Serveurs et service Client et lecture de flux au format XML et JSON –XML, SAX rappels cf. supports NFP121 –JSON

53 Android_Clients_Serveur 53 SAX XML Document John Doe Jane Doe SAX Objects Parser startDocument Parser startElement Parser startElement & characters Parser Parser endElement Parser startElement Parser startElement & characters Parser Parser endElement Parser endElement & endDocument

54 Android_Clients_Serveur 54 Implémenter les Handlers d'évènements du parseur DefaultHandler Il implémente ces différents Handler avec des méthodes vides, de sorte que l'on peut surcharger seulement celles qui nous intéressent.

55 Android_Clients_Serveur 55 org.xml.sax.ContentHandler Toutes les applications SAX doivent implanter un ContentHandler Méthodes : – public void startDocument() throws SAXException – public void endDocument() throws SAXException – public void startElement(String nspURI, String localName, String qName, Attributes atts) throws SAXException – public void characters(char[] ch, int start, int length) throws SAXException – …

56 Android_Clients_Serveur 56 Un exemple: les stations Vélib –http://www.velib.paris.fr –http://www.velib.paris.fr/service/cartohttp://www.velib.paris.fr/service/carto –http://www.velib.paris.fr/service/stationdetails/{number}http://www.velib.paris.fr/service/stationdetails/{number

57 Android_Clients_Serveur 57 Analyse des attributs de la balise marker en SAX -> méthode startElement

58 Android_Clients_Serveur Analyse du contenu de la balise station en SAX -> des méthodes startElement, endElement, characters

59 Android_Clients_Serveur 59 Les stations Vélib: suite Les Classes, un premier découpage –StationVelib, toutes les infos dune station, (adresse, longitude, latitude,…) –InfoStation, les informations comme le nombre de vélo et demplacements disponibles,... –ListeDesStationsVelib La gestion de la liste des stations –http://www.velib.paris.fr/service/cartohttp://www.velib.paris.fr/service/carto –http://www.velib.paris.fr/service/stationdetails/{number}http://www.velib.paris.fr/service/stationdetails/

60 Android_Clients_Serveur 60 Initialisation du « parser » class ParserXML extends DefaultHandler { public ParserXML(InputStream in) throws Exception{ SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); xr.setContentHandler(this); xr.parse(new InputSource(in)); }

61 Android_Clients_Serveur 61 startElement un extrait // Création dune instance de la classe StationVelib // depuis XML en Java public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); if(qName.equals("marker")){ StationVelib station = new StationVelib(); station.setName(attributes.getValue("name")); station.setNumber(Integer.parseInt(attributes.getValue("number"))); station.setAddress(attributes.getValue("address")); station.setLatitude(Double.parseDouble(attributes.getValue("lat"))); station.setLongitude(Double.parseDouble(attributes.getValue("lng"))); }

62 Android_Clients_Serveur 62 Une Info à chaque Station class ParserXML extends DefaultHandler { private StringBuffer current; // la valeur public ParserXML(int ID){ URL url = new URL(URL_VELIB_INFO + ID); SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser sp; sp = spf.newSAXParser(); XMLReader xr = sp.getXMLReader(); xr.setContentHandler(this); xr.parse(new InputSource(url.openStream())); }

63 Android_Clients_Serveur 63 A chaque noeud public void startElement (String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); current = new StringBuffer(); } public void characters (char[] ch, int start, int length) throws SAXException { super.characters(ch, start, length); current.append(new String(ch, start, length)); } public void endElement (String uri, String localName, String qName) throws SAXException { super.endElement(uri, localName, qName); if(qName.equals("available")){ available = Integer.parseInt(current.toString()); … }

64 Android_Clients_Serveur 64 XML plutôt verbeux, JSON plutôt concis JSON JavaScript Object Notation –www.json.org/ –JSONArray –JSONObject JSONArray jsonArray = new JSONArray(); JSONObject jsonObject = new JSONObject(); jsonObject.put( "name", "paul"); jsonObject.put("number", 1900); jsonArray.put(jsonObject); // pierre idem System.out.println(jsonArray.toString(2));

65 Android_Clients_Serveur 65 JSON, exemple paul et pierre [ { "name": "paul", "number": 1900 }, { "name": "pierre", "number": 1900 } ]

66 Android_Clients_Serveur 66 JSON Lecture InputStream in = …; Reader r = new InputStreamReader(in); BufferedReader br = new BufferedReader(r); StringBuffer sb = new StringBuffer(); String str; while((str = br.readLine()) != null) { sb.append(str); } r.close(); JSONArray jsonarray = new JSONArray(sb.toString()); JSONObject jsonObject = (JSONObject)jsonStations.get(i); Auditeur a = new Auditeur (); a.setName(jsonObject.getString("name")); …

67 Android_Clients_Serveur 67 StationVelib, lecture JSON JSONArray jsonStations = new JSONArray(sb.toString()); for(int i=0; i< jsonStations.length(); i++){ JSONObject jsonObject = (JSONObject)jsonStations.get(i); StationVelib st = new StationVelib(); st.setName(jsonObject.getString("name")); st.setNumber(jsonObject.getInt("number")); st.setAddress(jsonObject.getString("address")); st.setFullAddress(jsonObject.getString("fullAddress")); st.setLatitude(jsonObject.getDouble("lat")); st.setLongitude(jsonObject.getDouble("lng")); st.setOpen(jsonObject.getBoolean("open")); st.setBonus(jsonObject.getBoolean("bonus"));

68 Android_Clients_Serveur 68 Démonstration

69 Android_Clients_Serveur 69 Conclusion Discussions


Télécharger ppt "Android_Clients_Serveur 1 Connectivité, Web Services Orienté clients/serveur TCP et Bluetooth Notes de cours jean-michel Douin, douin au cnam point fr."

Présentations similaires


Annonces Google