Maison  >  Article  >  Java  >  Vous amener à maîtriser pleinement Java NIO (partage de résumé)

Vous amener à maîtriser pleinement Java NIO (partage de résumé)

WBOY
WBOYavant
2022-03-31 11:54:051801parcourir

Cet article vous apporte des connaissances pertinentes sur java, qui présente principalement les problèmes liés à NIO, y compris le noyau NIO, la comparaison entre BIO et NIO et la simple communication serveur-client via NIO. J'espère que cela sera utile à tout le monde.

Vous amener à maîtriser pleinement Java NIO (partage de résumé)

Étude recommandée : "Tutoriel Java"

1. Carte mentale Java

Vous amener à maîtriser pleinement Java NIO (partage de résumé)

2. Quel canal est l'essence du modèle d'E/S utilisé pour traiter les données ? L'envoi et la réception déterminent en grande partie les performances de la communication du programme.

Java prend en charge un total de trois modèles de programmation réseau : BIO, NIO, AIO


    BIO : synchronisation et blocage, le mode de mise en œuvre du service est une connexion et un thread, c'est-à-dire que lorsque le client a une demande de connexion, le serveur doit démarrer un thread pour le traitement.
  • NIO : synchrone et non bloquant, le mode d'implémentation du serveur permet à un thread de gérer plusieurs connexions de requêtes, c'est-à-dire que toutes les requêtes envoyées par le client seront enregistrées sur le multiplexeur, et le multiplexeur interroge jusqu'à ce que la connexion soit établie. La demande d’E/S sera traitée.
  • AIO : Asynchrone non bloquant, AIO introduit le concept de canal asynchrone, adopte le mode Proactor, simplifie l'écriture du programme, et démarre le thread seulement après une requête valide. Sa particularité est que le système d'exploitation le termine d'abord puis le notifie. le serveur.
  • 3. Scénarios d'application BIO, NIO, AIO

    La méthode BIO convient aux architectures avec un nombre de connexions relativement petit et fixe. Cette méthode a des exigences relativement élevées en ressources serveur et la concurrence est limitée aux applications. Avant JDK1.4 La seule option, mais la procédure est simple et facile à comprendre.
  • La méthode NIO convient aux architectures comportant un grand nombre de connexions et des connexions relativement courtes (opérations légères), telles que les serveurs de chat, les systèmes de barrage, les communications inter-serveurs, etc. La programmation est plus compliquée et JDK1.4 commence à la prendre en charge.
  • La méthode AIO est utilisée dans les architectures avec un grand nombre de connexions et des connexions relativement longues (opérations lourdes), comme les serveurs d'albums photos, qui font entièrement appel à l'OS pour participer à des opérations concurrentes. JDK7 commence à le prendre en charge
  • 4. Processus simple de programmation BIO

    Le serveur démarre un ServerSocket
  • Le client démarre le Socket pour communiquer avec le serveur. Par défaut, le serveur doit établir un. fil pour que chaque client puisse communiquer avec lui
  • Après que le client ait envoyé une demande, consultez d'abord le serveur pour voir s'il y a une réponse du fil. Sinon, elle attendra ou sera rejetée
  • S'il y en a. une réponse, le thread client attendra la fin de la requête avant de continuer à s'exécuter ;
  • 5. Noyau NIO

NIO Il y a trois parties principales : le sélecteur, le canal et le tampon.

NIO est une programmation orientée tampon ou orientée bloc. Les données sont lues dans un tampon qu'elles traiteront plus tard. Elles peuvent être déplacées d'avant en arrière dans le tampon en cas de besoin. , vous pouvez Fournit une mise en réseau non bloquante et hautement évolutive.

HTTP2.0 utilise la technologie de multiplexage pour permettre à la même connexion de traiter plusieurs requêtes simultanément, et le nombre de requêtes simultanées est plusieurs ordres de grandeur supérieur à celui de HTTP1.1.
En bref, NIO peut gérer plusieurs requêtes avec un seul thread.

6. Comparaison entre BIO et NIO

    BIO traite les données dans un flux, tandis que NIO traite les données dans un bloc. L'efficacité des E/S de bloc est bien supérieure à celle des E/S de flux
  •  ; BIO bloque Oui, NIO n'est pas bloquant ;
  • BIO fonctionne sur la base d'un flux d'octets et d'un flux de caractères, tandis que NIO fonctionne sur la base du canal et du tampon, et les données sont toujours lues du canal dans le tampon, ou écrites à partir de celui-ci. le tampon au canal. Le sélecteur est utilisé pour surveiller plusieurs événements de canal (tels que les demandes de connexion, l'arrivée de données, etc.), de sorte qu'un seul thread peut surveiller plusieurs canaux clients.
  • 7. Schéma des trois principes fondamentaux de NIO

Description de l'organigramme : Vous amener à maîtriser pleinement Java NIO (partage de résumé)

    Le sélecteur correspond à un thread, et un thread correspond à plusieurs canaux (connexions) 
  •  ; il y a trois canaux Inscrivez-vous au sélecteur //Programme ;
  • Chaque canal correspondra à un tampon
  • Le canal vers lequel le programme bascule est déterminé par les événements, et l'événement est un concept important ; Le sélecteur répondra en fonction des différents événements activés sur chaque canal ;
  • Buffer est un bloc de mémoire et il y a un tableau sur la couche inférieure
  • les données sont lues et écrites via Buffer, ce qui est le même que celui-ci. BIO. BIO est soit un flux d'entrée, soit un flux de sortie, qui ne peut pas être bidirectionnel, mais le tampon NIO peut être lu ou écrit et doit être commuté en utilisant la méthode flip ;
  • le canal est bidirectionnel et peut renvoyer l'état du système d'exploitation sous-jacent. Par exemple, Linux, le canal du système d'exploitation sous-jacent est bidirectionnel

8. Tampon

Un tampon est essentiellement un tampon qui peut être lu et lu. écrit Le bloc mémoire de données peut être compris comme un objet conteneur (y compris un tableau). Cet objet fournit un ensemble de méthodes pour faciliter l'utilisation du bloc mémoire. L'objet tampon possède des mécanismes intégrés qui peuvent suivre et enregistrer. l'état du tampon changeant de circonstances. Channel fournit un canal pour lire les données des fichiers et des réseaux, mais les données lues ou écrites doivent passer par Buffer.
Dans NIO, Buffer est une classe parent de niveau supérieur, qui est une classe abstraite.

1. Liste des sous-classes de Buffer couramment utilisées

  • ByteBuffer, stocke les données d'octets dans le tampon ;

  • ShortBuffer, stocke les données de chaîne dans le tampon

  • CharBuffer, stocke les données de caractères dans le tampon ; Area ;

  • IntBuffer, stocke les données entières dans le tampon ;

  • LongBuffer, stocke les données entières longues dans le tampon 

  • DoubleBuffer, stocke les décimales dans le tampon ; des mals à le tampon ;

  • 2. Quatre attributs majeurs du tampon

mark : mark

  • position : position, l'index du prochain élément à lire ou à écrire, à chaque fois que les données du tampon sont lu ou écrit La valeur sera modifiée à chaque fois pour préparer la prochaine lecture et écriture.

  • limite : indique le point final actuel du tampon. Les opérations de lecture et d'écriture ne peuvent pas être effectuées sur les positions où le tampon dépasse la limite. Et la limite peut être modifiée

  • capacité : capacité, c'est-à-dire la quantité maximale de données pouvant être hébergées ; elle est définie lors de la création du tampon et ne peut pas être modifiée ;

3. L'API introduite lorsque le tampon est couramment utilisé apiVous amener à maîtriser pleinement Java NIO (partage de résumé)

JDK1.4

public final int capacité( )//renvoie la capacité de ce tampon

public final int position( ) / /Renvoyer la position de ce tampon
  • public final Buffer position (int newPositio)//Définir la position de ce buffer
  • public final int limit()//Renvoyer la limite de ce buffer
  • public final Buffer limit (int newLimit )//Définir la limite de ce tampon
  • public final Buffer mark( )//Définir une marque à la position de ce tampon
  • public final Buffer reset( )//Réinitialiser la position de ce tampon à la position précédemment marquée
  • public final Buffer clear( )//Effacer ce tampon, c'est-à-dire restaurer chaque marque à son état initial, mais les données ne sont pas réellement effacées et les opérations ultérieures les écraseront
  • public final Buffer flip( )//Invert ce tampon
  • public final Buffer rewind( )//Rewind this buffer
  • public final int restant( )//Renvoyer le nombre d'éléments entre la position actuelle et la limite
  • public final boolean hasRemaining( )//Informer le courant position et s'il y a des éléments entre les restrictions
  • public abstract boolean isReadOnly();//Informer si ce tampon est un tampon en lecture seule
  • API introduite dans JDK1.6
public abstract boolean hasArray();// Informer si ce tampon a un tableau d'implémentation sous-jacent accessible

public abstract Object array();//Renvoie le tableau d'implémentation sous-jacent de ce tampon
  • public abstract int arrayOffset();//Renvoie le tableau d'implémentation sous-jacent de ce tampon Le décalage du premier élément tampon
  • public abstract boolean isDirect();//Informer si ce tampon est un tampon direct
9. Channel (channel)

Vous amener à maîtriser pleinement Java NIO (partage de résumé)

1. les canaux sont similaires aux flux

Les canaux peuvent lire et écrire en même temps, tandis que les flux ne peuvent que lire ou écrire ; Les canaux peuvent lire et écrire des données de manière asynchrone

Les canaux peuvent lire des données à partir de tampons et vous pouvez écrire des données dans le tampon

  • (2) Le flux dans BIO est unidirectionnel. Par exemple, l'objet FileInputStream ne peut lire que des données, tandis que le canal dans NIO est bidirectionnel et peut être une opération de lecture ou d'écriture.
  • (3) Channel est une interface dans NIO
  • (4) Les classes Channel couramment utilisées sont : FileChannel, DatagramChannel, ServerSocketChannel et SocketChannel. ServerSocketChanne est similaire à ServerSocket et SocketChannel est similaire à Socket.
  • (5) FileChannel est utilisé pour la lecture et l'écriture de données de fichiers, DatagramChannel est utilisé pour la lecture et l'écriture de données UDP, ServerSocketChannel et SocketChannel sont utilisés pour la lecture et l'écriture de données TCP.
2. FileChannel



FileChannel est principalement utilisé pour effectuer des opérations d'E/S sur des fichiers locaux. Les méthodes courantes sont :

lire, lire les données du canal et les mettre dans le tampon

écrire, mettre le. buffer Les données de la zone sont écrites dans le canal
  • transferFrom, copiez les données du canal cible vers le canal actuel
  • transferTo, copiez les données du canal actuel vers le canal cible

3. Notes et détails sur Buffer et Channel

  • ByteBuffer prend en charge les types put et get. Quel que soit le type de données saisi, get doit utiliser le type de données correspondant pour le récupérer, sinon il peut y avoir une exception BufferUnderflowException. .

  • Vous pouvez convertir un Buffer normal en un Buffer en lecture seule.

  • NIO fournit également MappedByteBuffer, qui permet de modifier les fichiers directement en mémoire (mémoire en dehors du tas), et la synchronisation avec les fichiers est complétée par NIO.

  • NIO prend également en charge les opérations de lecture et d'écriture via plusieurs tampons (c'est-à-dire des tableaux de tampons), à savoir la diffusion et le rassemblement.

10. Sélecteur(sélecteur)

1. Introduction de base

  • NIO de Java, utilisant la méthode IO non bloquante. Vous pouvez utiliser un seul thread pour gérer plusieurs connexions client et vous utiliserez le sélecteur.

  • Selector peut détecter si un événement se produit sur plusieurs canaux enregistrés. Si un événement se produit, l'événement est obtenu et chaque événement est traité en conséquence. De cette manière, un seul thread peut être utilisé pour gérer plusieurs canaux, c'est-à-dire pour gérer plusieurs connexions et requêtes.

  • Seulement lorsque la connexion/le canal a réellement un événement de lecture ou d'écriture, la lecture et l'écriture seront effectuées, ce qui réduit considérablement la surcharge du système, et il n'est pas nécessaire de créer un thread pour chaque connexion et de maintenir plusieurs threads.

  • Évitez la surcharge causée par le changement de contexte entre plusieurs threads.

2. Méthodes liées au sélecteur

  • open();//Obtenir un objet sélecteur

  • select(long timeout);//Surveiller tous les canaux enregistrés lorsqu'ils contiennent des opérations d'E/S Lorsque cela est possible , ajoutez la SelectionKey correspondante à la collection interne et renvoyez-la. Les paramètres sont utilisés pour définir le délai d'attente

  • selectedKeys( //Obtenir toutes les SelectionKeys de la collection interne.

3. Notes

La fonction ServerSocketChannel dans NIO est similaire à ServerSocket et la fonction SocketChannel est similaire à Socket.

11. Communication serveur-client simple via NIO

1. Serveur

package com.nezha.guor.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.*;import java.util.Iterator;public class NioServer {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private static final int PORT = 8080;

    public NioServer() {
        try {
            //获得选择器
            selector = Selector.open();
            serverSocketChannel =  ServerSocketChannel.open();
            //绑定端口
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            //设置非阻塞模式
            serverSocketChannel.configureBlocking(false);
            //将该ServerSocketChannel 注册到selector
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        }catch (IOException e) {
            System.out.println("NioServer error:"+e.getMessage());
        }
    }

    public void listen() {

        System.out.println("监听线程启动: " + Thread.currentThread().getName());
        try {
            while (true) {
                int count = selector.select();
                if(count > 0) {
                    //遍历得到selectionKey集合
                    Iterator<selectionkey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();

                        if(key.isAcceptable()) {
                            SocketChannel sc = serverSocketChannel.accept();
                            sc.configureBlocking(false);
                            sc.register(selector, SelectionKey.OP_READ);
                            System.out.println(sc.getRemoteAddress() + " 上线 ");
                        }
                        //通道发送read事件,即通道是可读的状态
                        if(key.isReadable()) {
                            getDataFromChannel(key);
                        }
                        //当前的key 删除,防止重复处理
                        iterator.remove();
                    }
                } else {
                    System.out.println("等待中");
                }
            }
        }catch (Exception e) {
            System.out.println("listen error:"+e.getMessage());
        }
    }

    private void getDataFromChannel(SelectionKey key) {
        SocketChannel channel = null;
        try {
            channel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            int count = channel.read(buffer);
            //根据count的值做处理
            if(count > 0) {
                String msg = new String(buffer.array());
                System.out.println("来自客户端: " + msg);

                //向其它的客户端转发消息(排除自己)
                sendInfoToOtherClients(msg, channel);
            }
        }catch (IOException e) {
            try {
                System.out.println(channel.getRemoteAddress() + " 离线了");
                //取消注册
                key.cancel();
            }catch (IOException ex) {
                System.out.println("getDataFromChannel error:"+ex.getMessage());
            }
        }finally {
            try {
                channel.close();
            }catch (IOException ex) {
                System.out.println("channel.close() error:"+ex.getMessage());
            }
        }
    }

    //转发消息给其它客户(通道)
    private void sendInfoToOtherClients(String msg, SocketChannel self ) throws  IOException{
        System.out.println("服务器转发消息中...");
        System.out.println("服务器转发数据给客户端线程: " + Thread.currentThread().getName());
        //遍历 所有注册到selector 上的 SocketChannel,并排除 self
        for(SelectionKey key: selector.keys()) {
            Channel targetChannel = key.channel();

            //排除自己
            if(targetChannel instanceof  SocketChannel && targetChannel != self) {
                SocketChannel dest = (SocketChannel)targetChannel;
                //将信息存储到buffer
                ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
                //将buffer数据写入通道
                dest.write(buffer);
            }
        }
    }

    public static void main(String[] args) {
        //创建服务器对象
        NioServer nioServer = new NioServer();
        nioServer.listen();
    }}</selectionkey>

2. Client

package com.nezha.guor.nio;import 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.SocketChannel;import java.util.Iterator;import java.util.Scanner;public class NioClient {
    private final int PORT = 8080; //服务器端口
    private Selector selector;
    private SocketChannel socketChannel;
    private String username;

    public NioClient() throws IOException {
        selector = Selector.open();
        socketChannel = socketChannel.open(new InetSocketAddress("127.0.0.1", PORT));
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //将channel注册到selector
        socketChannel.register(selector, SelectionKey.OP_READ);
        username = socketChannel.getLocalAddress().toString().substring(1);
        System.out.println(username + " is ok...");
    }

    //向服务器发送消息
    public void sendInfo(String info) {
        info = username + " 说:" + info;
        try {
            socketChannel.write(ByteBuffer.wrap(info.getBytes()));
        }catch (IOException e) {
            System.out.println("sendInfo error:"+e.getMessage());
        }
    }

    //读取从服务器端回复的消息
    public void readInfo() {
        try {
            int readChannels = selector.select();
            if(readChannels > 0) {
                Iterator<selectionkey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    if(key.isReadable()) {
                        //得到相关的通道
                        SocketChannel sc = (SocketChannel) key.channel();
                        //得到一个Buffer
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        //读取
                        sc.read(buffer);
                        //把读到的缓冲区的数据转成字符串
                        String msg = new String(buffer.array());
                        System.out.println(msg.trim());
                    }
                }
                iterator.remove(); //删除当前的selectionKey, 防止重复操作
            } else {
                System.out.println("没有可以用的通道...");
            }
        }catch (Exception e) {
            System.out.println("readInfo error:"+e.getMessage());
        }
    }

    public static void main(String[] args) throws Exception {
        NioClient nioClient = new NioClient();
        new Thread() {
            public void run() {
                while (true) {
                    nioClient.readInfo();
                    try {
                        Thread.currentThread().sleep(2000);
                    }catch (InterruptedException e) {
                        System.out.println("sleep error:"+e.getMessage());
                    }
                }
            }
        }.start();

        //发送数据给服务器端
        Scanner scanner = new Scanner(System.in);

        while (scanner.hasNextLine()) {
            nioClient.sendInfo(scanner.nextLine());
        }
    }}</selectionkey>

3. Etude : "Tutoriel Java

"

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer