1. Introduction à la bibliothèque NIO
1. Tampon tampon
Un tampon est un objet qui contient certaines données à écrire et à lire.
Dans NIO, toutes les données sont traitées dans un tampon. Lors de la lecture de données, il est lu directement du canal vers le tampon et lors de l'écriture de données, il est également écrit du tampon vers le canal.
Un tampon est essentiellement un tableau, généralement un tableau d'octets (bytebuffer) ou d'autres types de tableaux. De plus, le tampon fournit également des informations telles que l'accès structuré aux données et la maintenance des emplacements de lecture et d'écriture.
La relation d'héritage de la classe tampon est indiquée dans la figure ci-dessous:
2. Channel
Le canal est un canal où les données réseau sont lues et écrites via le canal. La différence entre un canal et un flux est que le canal est bidirectionnel (le canal peut être utilisé pour lire et écrire ce dernier en même temps), et le flux ne bouge que dans une seule direction.
Les canaux peuvent être à peu près divisés en deux catégories: selectableChannel pour la lecture et l'écriture du réseau (Serversocketchannel et socketchannel sont leurs sous-classes) et FileChannel pour les opérations de fichiers.
L'exemple suivant montre le fichier à l'aide de FileChannel pour écrire des données dans un fichier, lire les données à partir d'un fichier et copier les données de fichier dans un autre fichier:
classe publique Niotest {public static void main (String [] args) lève ioException {copyfile (); } // Copiez le fichier privé static void copyfile () {fileInputStream dans = null; FileoutputStream out = null; try {in = new FileInputStream ("src / main / java / data / in-data.txt"); out = new FileOutputStream ("src / main / java / data / out-data.txt"); FileChannel Inchannel = in.getChannel (); FileChannel Outchannel = out.getChannel (); ByteBuffer Buffer = ByteBuffer.AllOcy (1024); int bytesRead = inchannel.read (tampon); while (bytesRead! = - 1) {buffer.flip (); ouchannel.write (tampon); buffer.clear (); bytesRead = inchannel.read (tampon); }} catch (filenotfoundException e) {// TODO Block de catch généré automatiquement e.printStackTrace (); } catch (ioException e) {// TODO Bloc de capture généré automatiquement e.printStackTrace (); }} // Écriture de fichier privé static void writeFileNio () {try {randomaccessfile fout = new randomaccessfile ("src / main / java / data / nio-data.txt", "rw"); FileChannel fc = fout.getChannel (); ByteBuffer Buffer = ByteBuffer.AllOcy (1024); buffer.put ("hi123" .getBytes ()); buffer.flip (); essayez {fc.write (tampon); } catch (ioException e) {// TODO Bloc de capture généré automatiquement e.printStackTrace (); }} catch (filenotfoundException e) {// TODO Block de catch généré automatiquement e.printStackTrace (); }} // Lire Fichier Private Static void readFileNio () {FileInputStream FileInputStream; essayez {fileInputStream = new FileInputStream ("src / main / java / data / nio-data.txt"); FileChannel FileChannel = fileInputStream.getChannel (); // Get Channel de fileInputStream byteBuffer byteBuffer = byteBuffer.Allocate (1024); // Créer un tampon int bytesRead = fileChannel.read (bytebuffer); // Positionr = Position = Position = 0; * / bytebuffer.flip (); // HasReMinging (): Informez s'il existe un élément entre la position actuelle et la limite while (bytebuffer.hasreMinging ()) {System.out.print ((char) bytebuffer.get ()); } / * * Effacer le tampon * position = 0; * limite = capacité; * / bytebuffer.clear (); bytesRead = filechannel.read (bytebuffer); }} catch (filenotfoundException e) {// TODO Block de catch généré automatiquement e.printStackTrace (); } catch (ioException e) {// TODO Bloc de capture généré automatiquement e.printStackTrace (); }}}3. Sélecteur multiplexeur
Le multiplexeur offre la possibilité de sélectionner des tâches prêtes. Le sélecteur interrogera constamment le canal enregistré dessus. Si un canal envoie un événement de lecture ou d'écriture, le canal sera à l'état prêt et sera interrogé par le sélecteur. Ensuite, l'ensemble des canaux prêts peut être obtenu via le SelectionKey pour effectuer des opérations d'E / S ultérieures.
Un sélecteur de multiplexeur peut interroger plusieurs canaux en même temps. Étant donné que JDK utilise Epoll au lieu de l'implémentation de sélection traditionnelle, il n'a pas la limite de la poignée de connexion maximale 1024/2048, ce qui signifie qu'un seul thread est tenu d'être responsable du scrutin du sélecteur et peut accéder à des milliers de clients. Le modèle est illustré dans la figure ci-dessous:
Traitez un sélecteur avec un seul thread. Pour utiliser le sélecteur, vous devez enregistrer le canal avec Selector, puis appeler sa méthode SELECT (). Cette méthode se bloque jusqu'à ce qu'un canal enregistré ait des événements prêts. Une fois cette méthode renvoyée, le thread peut traiter ces événements, tels que de nouvelles connexions, une réception de données, etc.
Note:
1. Quel modèle sélectionné?
SELECT est un mécanisme de déclenchement d'événements, qui déclenche le traitement lorsque l'événement d'attente se produit, et est principalement utilisé pour le traitement du client par l'implémentation Linux des serveurs.
Il peut détecter simultanément un ensemble de dispositifs IO non bloquants qui prennent en charge la non-localisation, qu'il y ait des événements (tels que la sortie d'erreur de grande priorité lisible, écrite et élevée, etc.) jusqu'à ce qu'un appareil déclenche un événement ou dépasse le temps d'attente spécifié. Autrement dit, leur responsabilité n'est pas de faire IO, mais d'aider l'appelant à trouver l'appareil actuellement prêt.
2. Quel est le modèle Epoll?
L'idée de conception d'Epoll est de diviser le fonctionnement unique de Select / Poll en 1 epoll_create + multiple epoll_ctrl + une attente. De plus, le noyau a ajouté un système de fichiers "EventPollfs" pour les opérations EPOLL. Chaque ou plusieurs descripteurs de fichiers à surveiller ont un nœud inode du système de fichiers EventPollfs EventSlfs correspondant, et les informations principales sont stockées dans la structure EventPoll. Les informations importantes des fichiers surveillés sont stockés dans la structure Epitem. Ils sont donc une relation un-à-plusieurs.
2. Développement côté serveur NIO
Description de la fonction: Allumez le côté serveur et envoyez Hello String à chaque client d'accès.
Il existe plusieurs étapes principales pour utiliser NIO pour le développement côté serveur:
1. Créez des serveurs de serveur et configurez-le en mode non bloquant
Serversocketchannel = serversocketchannel.open (); Serversocketchannel.configureBlocking (false);
2. Bonnez-vous à écouter et à configurer les paramètres TCP, tels que la taille du arriéré
Serversocketchannel.socket (). Bind (new IneTSocketAddress (8080));
3. Créez un fil d'E / S indépendant pour interroger le sélecteur multiplexeur
4. Créez un sélecteur, enregistrez le serveurs de latchette que vous avez créé précédemment au sélecteur et écoutez pour selectionKey.Accept
sélecteur = sélecteur.open (); SERVERSOCHETHETCHANNEL.REGISTER (Selector, SelectionKey.op_Accept);
5. Démarrez le thread d'E / S, exécutez la méthode sélector.select () dans le corps de la boucle et interroger le canal prêt
tandis que (true) {try {// select () bloque jusqu'à ce qu'au moins un canal soit prêt sur l'événement que vous avez enregistré // s'il n'y a pas de canal prêt, il bloquera ici // select (long timeout) est le même que select (), sauf qu'il bloquera le délai d'attente des millisecondes (paramètres). Selector.Select (); } catch (ioException e) {// TODO Bloc de capture généré automatiquement e.printStackTrace (); casser; }}6. Lors de l'interrogation du canal dans l'état prêt, il doit être jugé. S'il s'agit de l'état OP_ACcept, cela signifie qu'il s'agit d'un nouvel accès client. Appelez ensuite ServersocketChannel.Accept () pour accepter le nouveau client.
// Renvoie le Ready SelectionKey, puis itérez pour exécuter Set <SelectionKey> readkeys = sélector.selectedKeys (); for (iterator <lelectionKey> it = readkeys.iterator (); it.hasnext ();) {selectionKey key = it.next (); it.remove (); essayez {if (key.isacceptable ()) {serversOCHECTALNEL Server = (serversocketchannel) key.channel (); Socketchannel client = server.accept (); client.ConfigureBlocking (false); client.Register (Selector, SelectionKey.op_Write); } else if (key.iswitable ()) {socketchannel client = (socketchannel) key.channel (); ByteBuffer Buffer = ByteBuffer.ALLOCK (20); String str = "Hello"; buffer = bytebuffer.wrap (str.getBytes ()); client.write (tampon); key.cancel (); }} catch (ioException e) {e.printStackTrace (); key.cancel (); essayez {key.Channel (). Close (); } catch (ioException e1) {// TODO GÉNÉRÉ AUTO GÉNÉRÉ BLOC E1.PRINTSTACKTRACE (); }}}7. Définissez le nouveau lien client Socketchannel en mode non bloquant et configurez d'autres paramètres TCP.
if (key.isacceptable ()) {serversocketchannel server = (serversocketchannel) key.channel (); Socketchannel client = server.accept (); client.ConfigureBlocking (false); ...}8. Enregistrez Socketchannel au sélecteur et écoutez OP_WRITE
client.Register (Selector, SelectionKey.op_Write);
9. Si le canal sondé est OP_WRITE, cela signifie que les données doivent être écrites dans le sockchannel, alors l'objet ByteBuffer est construit et le paquet de données est écrit.
else if (key.iswitable ()) {socketchannel client = (socketchannel) key.channel (); ByteBuffer Buffer = ByteBuffer.ALLOCK (20); String str = "Hello"; buffer = bytebuffer.wrap (str.getBytes ()); client.write (tampon); key.cancel (); }Le code complet est le suivant:
Importer java.io.ioException; import java.net.inetsocketAddress; import java.nio.bytebuffer; import java.nio.channels.selectionkey; import java.nio.channels.selector; import java.nio.channels.selector; import java.nio.channels.serversock java.nio.channels.socketchannel; import java.util.iterator; import java.util.set; public class serversocketchanneldemo {public static void Main (string [] args) {serversocketchannel = serversocketchannel; sélecteur sélecteur = null; try {serversocketchannel = Serversocketchannel.open (); serversocketchannel.configureblocking (false); serversocketchannel.socket (). Bind (new InetsocketAddress (8080)); Selectiony Blocke.printStackTrace ();} While (true) {try {// select () bloque jusqu'à ce qu'au moins un canal soit prêt sur l'événement que vous vous êtes inscrit // s'il n'y a pas de canal prêt, il bloquera ici tout le temps. //select(long Timeout) est le même que Select (), sauf qu'il bloquera le délai d'emplacement des millisecondes (paramètres). Selector.Select ();} Catch (ioException e) {// TODO Generated Catch Blocke.printStackTrace (); Break;} // Renvoie le Ready SelectionKey, puis itérera à exécuter Set <SelectionKey> Readkeys = Selector.SelectedKeys (); for (iterator <lelectionkey> it = readkeys.iterator (); it.hasnext ();););););) {SelectionKey Key = it.next (); it.reMove (); try {if (key.isacceptable ()) {serversocketchannel server = (serversocketchannel) key.channel (); socketchannel client = server.accept (); client.configureBlocking (false); Client.register (Selector, Selectionkey.op_write);} if (key.iswitable ()) {socketchannel client = (socketchannel) key.channel (); bytebuffer tamper = bytebuffer.Allocate (20); string str = "hello"; buffer = bytebuffer.wrap (str.getbytes ()); client.write (tampon); clés. gensNous utilisons Telnet localhost 8080 pour simuler plusieurs clients:
Les résultats de l'exécution du programme sont les suivants:
Résumer
Ce qui précède est toute l'explication détaillée du développement côté serveur Java Nio dans cet article, j'espère que cela sera utile à tout le monde. Les amis intéressés peuvent continuer à se référer à d'autres sujets connexes sur ce site. S'il y a des lacunes, veuillez laisser un message pour le signaler. Merci vos amis pour votre soutien pour ce site!