IFT2240 / IFT6815 JAVA et l'internet =================== J.Vaucher (nov. 2000, mod. mars 2003) Exemples du texte: http://www.iro.umontreal.ca/~vaucher/Java/Sockets/ ================= Inspire de "Java Examples in a Nutshell" de Flanagan, O'reilly http://www.oreilly.com/catalog/jenut/examples/ http://www.JSP.UMontreal.CA/~java/Doc/Nutshell/examples/ Voir ausi la documentation de Sun: ---------------------------------- http://java.sun.com/products/jdk/1.4/docs/api/Package-java.net.html INTRODUCTION ============= Ici nous donnons un bref apercu des outils fournis par Java pour acceder a l'Internet et au Web. L'Internet est beaucoup plus vieux que le WEB. C'est l'interconnexion physique des reseaux nationaux de transfert de donnees ainsi qu'un ensemble de normes permettant une communication coherente entre ordinateurs differents. Un service critique a ce niveau est celui des Bottins reseaux (DNS- Domain Name Servers) qui permettent de trouver l'adresse electronique d'une machine dont on connait le nom, p.e. "Phobos.iro.umontreal.ca". Le Web est un ensemble de services qui utilise l'Internet pour transferrer les donnees. La communication sur l'internet se fait par l'entremise de SOCKETS. Il y a des sockets "stream" qui marchent comme des connexion telephoniques, permettant l'echange sans erreur d'une suite d'octets. Il y a aussi des connexion "datagram" qui ressemblent a l'expedition de lettres entre correspondant. La communication par socket 'stream' utilise le format CLIENT-SERVEUR. Dans ce qui suit, nous traitons: 1) les sockets 'clients' 2) les sockets 'serveurs' 3) l'acces par socket a un serveur WWW avec le protocole Http 4) l'emploi de URL pour faciliter l'acces a des fichiers sur des serveurs WWW 5) communication par Datagrammes STREAM (tcp) SOCKETS ===================== Ces connexions permettent la transmission sans erreur d'une suite d'octets entre deux machines branchees sur l'Internet. - Pour ouvrir un socket vers un service STREAM, il faut avoir le nom de la machine et le numero du port sur lequel un serveur attend des communications. Exemples: Socket socket = new Socket(host, port); Socket diroHttp = new Socket("www.iro.umontreal.ca", 80); Chaque socket a un flux d'entree et un flux de sortie - equivalents a System.in et System.out - qu'on recupere a l'aide de getInputStream() et getOutputStream(). On peut utiliser ces flux directement pour l'ES de "bytes"; mais, en general, on va y ajouter un PrintWriter pour pouvoir ecrire avec "print" et un BufferedReader pour lire ligne par ligne avec "readLine()". Ouput avec "print": -------------------- PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); out.println("HTTP/1.0 200 "); // Version & status code out.println(); // End of response headers out.flush(); Lecture ligne par ligne (BufferedReader): ----------------------------------------- BufferedReader in = new BufferedReader(new InputStreamReader( socket.getInputStream())); String line; while((line = in.readLine()) != null) { if (line.length() == 0) break; out.println(line); } Lecture directe du Stream: -------------------------- InputStream from_server = socket.getInputStream(); byte[] buffer = new byte[4096]; int bytes_read; while((bytes_read = from_server.read(buffer)) != -1) Sysout.out.write(buffer, 0, bytes_read); SOCKET SERVEUR --------------- L'emploi des sockets par un Serveur est differente. D'abord on cree un "ServerSocket" avec un # de port PUBLIQUE. Ce socket, etrangement, ne sert pas a l'echange de donnees entre client et serveur; plutot, il sert a recevoir les "demandes" de connexion des clients. La methode "accept" attend la reception d'une connexion et cree un Socket "prive" pour la communication avec ce client. ServerSocket ss = new ServerSocket(port); // Port disp. si PORT=0 System.out.println("Receving on Port: " + ss.getLocalPort()); Socket client = ss.accept(); System.out.println(" Connexion de: " + client.getInetAddress()); BufferedReader in = new BufferedReader(new InputStreamReader( client.getInputStream())); ...etc... Addresses INTERNET: =================== InetAddress localAdr = InetAddress.getLocalHost(); String host = "derby"; InetAddress address = InetAddress.getByName(host); address.getHostName() ==> derby.jsp.umontreal.ca address.getHostAddress() ==> 132.204.45.26 Adresse a l'autre bout d'un Socket s: s.getInetAddress() Exemples: > SocketClient.java ======== > SocketServer.java COMMUNICATION AVEC UN SERVEUR WEB ================================== La communication sur le WWW se fait avec des Sockets STREAM. Par convention, un serveur WWW - comme Apache - cree un ServerSocket sur le port #80 et interagit avec des browsers selon le protocole HTTP dont les commandes de base sont tres simples: une ligne de texte debutant avec un mot-cle. Il est possible d'intergair directement avec un serveur WWW en creant un Socket sur le port #80 et en expediant une ligne de commande selon le protocole Http. Par exemple: String line; Socket diroHttp = new Socket("www.iro.umontreal.ca", 80); PrintWriter out = new PrintWriter(new OutputStreamWriter(diroHttp.getOutputStream())); BufferedReader in = new BufferedReader(new InputStreamReader(diroHttp.getInputStream())); out.print("GET /~vaucher/test.html\r\n"); while((line = in.readLine()) != null) out.println(line); Ceci est equivalent a utiliser un browser pour chercher l'URL: "http://www.iro.umontreal.ca/~vaucher/test.html". Si on ajoute le parametre "HTTP/1.0" a la commande, le serveur retourne d'abord un HEADER - de l'information sur le fichier (longueur, date, encodage) - puis le fichier lui-meme. La commande HEAD retourne le HEADER sans contenu: out.println("GET /~vaucher/test.html HTTP/1.0\r\n"); out.println("HEAD /~vaucher/test.html HTTP/1.0\r\n"); Dans ces deux dernier cas, il faut expedier une ligne blanche apres la commande. Ceci explique le "printLN" et le "\r\n". Le deuxieme 'token' de la premiere ligne retournee est un code qui indique si la requete est bonne (200) ou non (autre chose que 200). Exemples: On expedie >>> HEAD /~tintin HTTP/1.0 On recoit <<< HTTP/1.1 403 Forbidden On expedie >>> HEAD / http/1.0 On recoit <<< HTTP/1.1 200 OK URL et URLConnection ======================== Java a des classes qui simplifie la communication avec des serveurs WWW. La classe URL regroupe toutes les methodes disponibles sur un URL. URL u = new URL("http://www.iro.umontreal.ca:800/fred/xxx.html#a"); u.getHost() ==> www.iro.umontreal.ca u.getProtocol() ==> http u.getPort() ==> 800 (ou -1) si non specifie...= 80 u.getFile() ==> fred/xxx.html On peut ensuite s'en servir pour chercher le document designe par l'URL. Acces au contenu du URL ======================== URL url = new URL("http:..."); // Create the URL 1) InputStream in = url.openStream(); // Open a stream to it // Now copy bytes from the URL to the output stream byte[] buffer = new byte[4096]; int bytes_read; while((bytes_read = in.read(buffer)) != -1) System.out.write(buffer, 0, bytes_read); ------------------------------------------ 2) URLConnection link = url.openConnection(); BufferedReader inStream = new BufferedReader(new InputStreamReader(link.getInputStream())); while ((line = inStream.readLine()) != null) System.out.print(line); INFORMATION SUR URL ===================== On peut aussi acceder aux informations du HEADER HTTP. URLConnection c = url.openConnection(); c.connect(); Content Type: c.getContentType() Content Encoding: c.getContentEncoding() Content Length: c.getContentLength() Date: c.getDate() Last Modified: c.getLastModified() Expiration: c.getExpiration() Conversion: String, Bytes & Chars ================================== Les methodes de telecommunication de JAVA sont basees sur le transfert d'octets (byte) et il y a incompatibilite avec l'utilisation de String de caracteres UNICODE (char) dans le reste du langage. Voici comment convertir: byte[] buffer = new byte[2048]; String msg = "Ceci est une chaine"; byte[] message ; message = msg.getBytes(); // conversion String a byte[] String s1 = new String(message), // conversion byte[] a String s2 = new String( buffer, 0, 10 ); // partie d'un buffer DATAGRAMS ========= La communication par Datagramme est equivalente a l'expedition de lettres. A moins que le destinataire prenne la peine de nous expedier une reponse, on n'est pas assure les Datagrammes (lettre) se sont rendu a bon port ou s'ils sont arrives dans le meme ordre qu'on les avait expedies. mais disons que 99% du temps tout va bien. Autre difference avec les connexions TCP est qu'un meme socket peut etre utilise pour envoyer et recevoir des messages a plusieurs interlocuteurs. Finalement, avec le "multicast" un meme message peut etre recu par tous les process branche sur la meme adresse (mais ceci ne marche qu'a l'interieur d'un reseau local - comme celui de l'Universite). DatagramSocket dsock1 = new DatagramSocket(), // Port libre octroye dsock2 = new DatagramSocket(5555), // sur Port 5555 System.out.println("Server Port: " + dsock1.getLocalPort()); Packet : base sur tableau d'octets. Pour output, on ajoute 'adr' ======= du destinataire DatagramPacket packet = new DatagramPacket( buffer, buffer.length); DatagramPacket packetout = new DatagramPacket( buffer, buffer.length, address, port); Expedition: dsock1.send(packetout); Reception: dsock2.receive(packet); String contenu = new String(buffer,0,packet.getLength()); Host Name: packet.getAddress().getHostName() ); Internet address: packet.getAddress().getHostAddress() ); Sender Port: packet.getPort()); Reponse: dsock2.send( new DatagramPacket( "OK".getbytes(), "OK".length(), packet.getAddress(),packet.getPort()); MULTICAST DATAGRAMS (Host: 224.0.1.0 - 239.255.255.255) ========================================================= Exemple avec Host:port "225.0.0.66:4441" Expediteur: InetAddress group = InetAddress.getByName("225.0.0.66"); DatagramSocket sock = new DatagramSocket(); byte [] buf = "Allo".getBytes(); DatagramPacket pk = new DatagramPacket(buf, buf.length, group, 4441); sock . send( pk ); Reception: DatagramSocket sock = new DatagramSocket( 4441); sock.joinGroup( InetAddress.getByName("225.0.0.66") ); byte [] buf = new byte[256]; DatagramPacket pk = new DatagramPacket(buf, buf.length); sock . receive( pk ); System.out.println( new String( pk.getData() ); String reponse = new String( buf,0,pk.getLength()) ; // alternative