Programmation de Socket en Java: un tutoriel

ce tutoriel est une introduction à la programmation de socket en Java, en commençant par un simple exemple client-serveur démontrant les fonctionnalités de base de Java I/O. vous serez présenté à la fois le java.io paquet et NIO,/div>) API introduites dans Java 1.4. Enfin, vous verrez un exemple qui montre le réseau Java tel qu’implémenté à partir de Java 7 forward, dans NIO.2.,

La programmation de Socket se résume à deux systèmes communiquant entre eux. Généralement, la communication réseau se décline en deux versions: le protocole TCP (Transport Control Protocol) et le protocole UDP (User Datagram Protocol). TCP et UDP sont utilisés à des fins différentes et ont tous deux des contraintes uniques:

  • TCP est un protocole relativement simple et fiable qui permet à un client d’établir une connexion avec un serveur et aux deux systèmes de communiquer. En TCP, chaque entité sait que ses charges utiles de communication ont été reçues.,
  • UDP est un protocole sans connexion et est bon pour les scénarios où vous n’avez pas nécessairement besoin de chaque paquet pour arriver à sa destination, comme le streaming multimédia.

pour apprécier la différence entre TCP et UDP, réfléchissez à ce qui se passerait si vous diffusiez de la vidéo à partir de votre site web préféré et qu’il laissait tomber des images. Préférez-vous que le client ralentisse votre film pour recevoir les images manquantes ou préférez-vous que la vidéo continue à jouer? Les protocoles de streaming vidéo exploitent généralement UDP., Parce que TCP garantit la livraison, C’est le protocole de choix pour HTTP, FTP, SMTP, POP3, etc.

dans ce tutoriel, je vous présente la programmation de socket en Java. Je présente une série d’exemples client-serveur qui démontrent les fonctionnalités du framework D’e/s Java D’origine, puis je passe progressivement à l’utilisation des fonctionnalités introduites dans NIO.2.

sockets Java Old-school

dans les implémentations antérieures à NIO, le code de socket client TCP Java est géré par la classejava.net.Socket., Le code suivant ouvre une connexion à un serveur:

Socket socket = new Socket( server, port );

Une fois que notre instancesocket est connectée au serveur, nous pouvons commencer à obtenir des flux d’entrée et de sortie vers le serveur. Les flux d’entrée sont utilisés pour lire les données du serveur tandis que les flux de sortie sont utilisés pour écrire des données sur le serveur., Nous pouvons exécuter les méthodes suivantes pour obtenir des flux d’entrée et de sortie:

InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();

parce que ce sont des flux ordinaires, les mêmes flux que nous utiliserions pour lire et écrire dans un fichier, Nous pouvons les convertir sous la forme qui sert le mieux notre cas d’utilisation. Par exemple, on peut envelopper le OutputStream avec un PrintStream alors que l’on peut facilement écrire un texte avec des méthodes comme println()., Pour un autre exemple, on peut envelopper le InputStream avec un BufferedReader par un InputStreamReader, afin de faciliter la lecture du texte avec des méthodes comme readLine().

télécharger

le code Source de « programmation de Socket en Java: un tutoriel. »Créé par Steven Haines pour JavaWorld.

exemple de client de socket Java

passons en revue un court exemple qui exécute un GET HTTP sur un serveur HTTP., HTTP est plus sophistiqué que ne le permet notre exemple, mais nous pouvons écrire du code client pour gérer le cas le plus simple: Demander une ressource au serveur et le serveur renvoie la réponse et ferme le flux. Ce cas nécessite les étapes suivantes:

  1. créer un socket pour le serveur web écoutant sur le port 80.
  2. Obtenir PrintStream pour le serveur et d’envoyer la demande GET PATH HTTP/1.0, où PATH est la ressource demandée sur le serveur., Par exemple, si nous voulions ouvrir la racine d’un site web, alors la voie serait /.
  3. obtenez un InputStream au serveur, enveloppez-le avec un BufferedReader et lisez la réponse ligne par ligne.

la liste 1 affiche le code source de cet exemple.

Liste 1. SimpleSocketClientExample.java

la liste 1 Accepte deux arguments de ligne de commande: le serveur auquel se connecter (en supposant que nous nous connectons au serveur sur le port 80) et la ressource à récupérer., Il crée un Socket qui pointe vers le serveur et spécifie explicitement le port 80. Ensuite, il exécute la commande:

GET PATH HTTP/1.0

Par exemple:

GET / HTTP/1.0

Ce qui s’est passé?

lorsque vous récupérez une page web à partir d’un serveur web, tel que, le client HTTP utilise des serveurs DNS pour trouver l’adresse du serveur: il commence par demander au serveur de domaine de premier niveau le domainecom où le serveur de noms de, Ensuite, il demande au serveur de noms de domaine l’adresse IP (ou les adresses) pour . Ensuite, il ouvre un socket sur ce serveur sur le port 80. (Ou, si vous souhaitez définir un port différent, vous pouvez le faire en ajoutant une virgule suivi par le numéro de port, par exemple: :8080. Enfin, le client HTTP exécute le spécifiée méthode HTTP, tels que le GET, POST, PUT, DELETE, HEAD ou OPTI/ONS. Chaque méthode a sa propre syntaxe., Comme indiqué dans les extraits de code ci-dessus, la méthode GET nécessite un chemin suivi par HTTP/version number et une ligne vide. Si nous voulions ajouter des en-têtes HTTP, Nous aurions pu le faire avant d’entrer dans la nouvelle ligne.

Dans le Listing 1, nous avons récupéré un OutputStream et enveloppé dans un PrintStream afin que nous puissions plus facilement exécuter notre base de texte commandes., Notre code obtenu un InputStream, enveloppé dans une InputStreamReader, qui l’a converti en un Reader, et ensuite enveloppé que dans un BufferedReader. Nous avons utilisé la PrintStream pour exécuter notre GET, puis utilisé la BufferedReader pour lire la réponse ligne par ligne jusqu’à ce que nous recevions une réponse null, indiquant que le socket avait été fermé.,

exécutez maintenant cette classe et passez-lui les arguments suivants:

java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /

Vous devriez voir une sortie similaire à ce qui est ci-dessous:

Cette sortie affiche une page de test sur le site Web de JavaWorld. Il a répondu qu’il parle HTTP version 1.1 et la réponse est 200 OK.

Java serveur socket exemple

Nous avons couvert le côté client et, heureusement, l’aspect de la communication de la partie serveur est tout aussi facile., D’un point de vue simpliste, le processus est le suivant:

  1. créer un ServerSocket, en spécifiant un port sur lequel écouter.
  2. appelez la méthodeServerSocketaccept() pour écouter sur le port configuré pour une connexion client.
  3. Lorsqu’un client se connecte au serveur, la balise accept() méthode renvoie un Socket par le biais de laquelle le serveur peut communiquer avec le client., C’est la même classe Socket que nous avons utilisée pour notre client, donc le processus est le même: obtenir un InputStream à lire depuis le client et un OutputStream écrire sur le client.
  4. Si votre serveur doit être évolutif, vous voudrez passer le Socket à un autre thread à traiter afin que votre serveur puisse continuer à écouter des connexions supplémentaires.
  5. appelez à nouveau la méthode ServerSocketaccept() pour écouter une autre connexion.,

comme vous le verrez bientôt, la gestion de ce scénario par NIO serait un peu différente., Pour l’instant, cependant, nous pouvons créer directement un ServerSocket en lui passant un port à écouter (plus d’informations sur ServerSocketFactorys dans la section suivante):

ServerSocket serverSocket = new ServerSocket( port );

Et maintenant nous pouvons accepter les connexions entrantes via le accept() méthode:

Socket socket = serverSocket.accept();// Handle the connection ...

programmation multithread avec des sockets Java

la liste 2, ci-dessous, rassemble tout le code du serveur jusqu’à présent dans un exemple légèrement plus robuste qui utilise des threads pour gérer plusieurs requêtes., Le serveur affiché est un serveur echo, ce qui signifie qu’il renvoie tout message qu’il reçoit.

bien que l’exemple de la liste 2 ne soit pas compliqué, il anticipe certains de ce qui se passe dans la section suivante sur NIO. Portez une attention particulière à la quantité de code de threading que nous devons écrire afin de construire un serveur capable de gérer plusieurs demandes simultanées.

Liste 2. SimpleSocketServer.java

dans la liste 2, Nous créons une nouvelle instanceSimpleSocketServer et démarrons le serveur., Cela est nécessaire parce que le SimpleSocketServer extends Thread pour créer un nouveau thread pour gérer le blocage accept() appel que vous voyez dans le read() méthode. La méthode run() se trouve dans une boucle acceptant les demandes des clients et créant des threads RequestHandler pour traiter la demande. Encore une fois, il s’agit d’un code relativement simple, mais implique également une bonne quantité de programmation threadée.,

Notez également que le RequestHandler gère la communication client comme le code de la liste 1: il encapsule le OutputStream avec un PrintStream pour faciliter les Écritures faciles et, de même, encapsule le InputStream div>avec un BufferedReader pour une lecture facile. En ce qui concerne un serveur, il lit les lignes du client et les renvoie au client. Si le client envoie une ligne vide puis la conversation est terminée et le RequestHandler ferme le socket.

NIO et NIO.,2

Pour de nombreuses applications, le modèle de base de programmation de socket Java que nous venons d’Explorer est suffisant. Pour les applications impliquant des e/s plus intensives ou des entrées/sorties asynchrones, vous voudrez vous familiariser avec les API non bloquantes introduites dans Java NIO Et NIO.2.

le package JDK 1.4 NIO offre les fonctionnalités clés suivantes:

  • Les canaux sont conçus pour prendre en charge les transferts en masse d’un tampon NIO à un autre.
  • Les tampons représentent un bloc de mémoire contigu interfacé par un simple ensemble d’opérations.,
  • L’Entrée/Sortie Non bloquante est un ensemble de classes qui exposent des canaux à des sources d’E / S courantes telles que des fichiers et des sockets.

lors de la programmation avec NIO, vous ouvrez un canal vers votre destination, puis lisez les données dans un tampon à partir de la destination, écrivez les données dans un tampon et envoyez-les à votre destination., Nous allons plonger dans la configuration d’un socket et y obtenir un canal sous peu, mais passons d’abord en revue le processus d’utilisation d’un tampon:

  1. écrire des données dans un tampon
  2. appeler la flip() méthode pour le préparer à la lecture
  3. Lire 101f25f528″>ou compact() méthode pour le préparer à recevoir plus de données

lorsque des données sont écrites dans le tampon, le tampon connaît la quantité de données qui y sont écrites., Il conserve trois propriétés, dont les significations diffèrent si le tampon est en mode Lecture ou en mode écriture:

  • Position: en mode écriture, la position initiale est 0 et il maintient la position actuelle en cours d’écriture dans le tampon; après avoir retourné un tampon pour le mettre en mode Lecture, il réinitialise la position à 0 et maintient la position actuelle dans le tampon en cours de lecture,
  • capacité: la taille fixe du tampon
  • limite: en mode écriture, la limite définit la quantité de données pouvant être écrites dans le tampon; dans mode de lecture, la limite définit la quantité de données pouvant être lues à partir du tampon.,

démonstration D’E / S Java: serveur Echo avec NIO.2

NIO.2, qui a été introduit dans JDK 7, étend les bibliothèques D’E/S non bloquantes de Java pour ajouter la prise en charge des tâches du système de fichiers, telles que le package java.nio.file et la classe java.nio.file.Path et expose une nouvelle API de système de fichiers. Avec ce contexte à l’esprit, écrivons un nouveau serveur Echo en utilisant NIO.2 AsynchronousServerSocketChannel.

AsynchronousServerSocketChannel fournit un canal asynchrone non bloquant pour les sockets d’écoute orientés flux., Pour l’utiliser, nous exécutons d’abord sa méthode statique open() puis bind() sur un port spécifique. Ensuite, nous allons exécuter sa méthodeaccept(), en lui passant une classe qui implémente l’interfaceCompletionHandler. Le plus souvent, vous trouverez ce gestionnaire créé en tant que classe interne anonyme.

la liste 3 montre le code source de notre nouveau serveur Echo asynchrone.

Liste 3. SimpleSocketServer.,java

dans la liste 3, Nous créons d’abord un nouveau AsynchronousServerSocketChannel, puis le Lions au port 5000:

 final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));

à partir de ce AsynchronousServerSocketChannel, nous invoquons accept() pour lui dire de commencer à écouter les connexions, en lui passant une instance CompletionHandler personnalisée. Lorsque nous invoquons accept(), il revient immédiatement., Notez que cet exemple est différent de la classeServerSocket dans la liste 1; alors que la méthodeaccept() bloquée jusqu’à ce qu’un client s’y connecte, la méthodeAsynchronousServerSocketChannelaccept() la gère pour nous.

Le gestionnaire d’achèvement

Notre prochaine responsabilité est de créer un CompletionHandler de classe et de fournir une implémentation de la balise completed() et failed() méthodes., La méthode completed() est appelée lorsque le AsynchronousServerSocketChannel reçoit une connexion d’un client et inclut un AsynchronousSocketChannel vers le client. La méthode completed() accepte d’abord la connexion à partir du AsynchronousServerSocketChannel, puis commence à communiquer avec le client. La première chose qu’elle fait est d’écrire un message « Hello »: Il construit une chaîne de caractères, le convertit en un tableau d’octets, et passe ensuite à la ByteBuffer.wrap() pour construire un ByteBuffer., La balise ByteBuffer peuvent alors être transmises AsynchronousSocketChannel‘s write() méthode.

Pour lire de la part du client, nous créons un nouveau ByteBuffer en invoquant son allocate(4096) (ce qui crée un tampon 4K), puis nous invoquons le AsynchronousSocketChannel‘s read() méthode. La balise read() retourne un Future<Integer> sur laquelle nous pouvons invoquer get() pour récupérer le nombre d’octets lus à partir du client., Dans cet exemple, nous passons get() une valeur de délai d’attente de 20 secondes: si nous n’obtenons pas de réponse en 20 secondes, la méthode get() lancera un TimeoutException. Notre règle pour ce serveur echo est que si nous observons 20 secondes de silence, nous terminons la conversation.

ensuite, nous vérifions la position du tampon, qui sera l’emplacement du dernier octet reçu du client. Si le client envoie une ligne vide puis nous recevons deux octets: un retour chariot et un saut de ligne., La vérification garantit que si le client envoie une ligne vide, nous la Prenons comme un indicateur que le client a terminé la conversation. Si nous avons des données significatives, nous appelons la méthodeByteBufferflip() pour la préparer à la lecture. Nous avons créer un temporaire tableau d’octets pour contenir le nombre d’octets lus à partir du client et ensuite invoquer la balise ByteBuffer‘s get() pour charger les données dans ce tableau d’octets. Enfin, nous convertissons le tableau d’octets en chaîne en créant une nouvelle instance String., Nous renvoyons la ligne au client en convertissant la chaîne en un tableau d’octets, en la transmettant à la méthode ByteBuffer.wrap() et en appelant la méthode AsynchronousSocketChannelwrite(). Maintenant, nous clear() le ByteBuffer, qui rappellent signifie qu’il repositionne le position à zéro et met la balise ByteBuffer dans le mode d’écriture, et puis nous lire la ligne suivante de la part du client.,

la seule chose à savoir est que la méthode main(), qui crée le serveur, configure également une minuterie de 60 secondes pour que l’application continue à fonctionner. Parce que la méthode AsynchronousSocketChannelaccept() renvoie immédiatement, si nous n’avons pas le Thread.sleep() alors notre application s’arrêtera immédiatement.,

pour tester cela, lancez le serveur et connectez-vous à l’aide d’un client telnet:

telnet localhost 5000

envoyez quelques chaînes au serveur, observez qu’elles vous sont renvoyées, puis envoyez une ligne vide pour terminer la conversation.

en conclusion

dans cet article, j’ai présenté deux approches de la programmation de socket avec Java: l’approche traditionnelle introduite avec Java 1.0 et les plus récents, non bloquants NIO Et NIO.2 approches introduites dans Java 1.4 et Java 7, respectivement., Vous avez vu plusieurs itérations D’un client de socket Java et D’un exemple de serveur de socket Java, démontrant à la fois l’utilité des E/S Java de base et certains scénarios où les e/s non bloquantes améliorent le modèle de programmation de socket Java. En utilisant des e/s non bloquantes, vous pouvez programmer des applications en réseau Java pour gérer plusieurs connexions simultanées sans avoir à gérer plusieurs collections de threads. Vous pouvez également profiter de la nouvelle évolutivité du serveur intégrée à NIO Et NIO.2.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *