Heim >Java >javaLernprogramm >Bringt Sie dazu, Java NIO vollständig zu beherrschen (Zusammenfassungsfreigabe)

Bringt Sie dazu, Java NIO vollständig zu beherrschen (Zusammenfassungsfreigabe)

WBOY
WBOYnach vorne
2022-03-31 11:54:051822Durchsuche

Dieser Artikel vermittelt Ihnen relevantes Wissen über Java, das hauptsächlich NIO-bezogene Themen vorstellt, einschließlich des NIO-Kerns, des Vergleichs zwischen BIO und NIO und der einfachen Server-Client-Kommunikation über NIO.

Bringt Sie dazu, Java NIO vollständig zu beherrschen (Zusammenfassungsfreigabe)

Empfohlene Studie: „Java-Tutorial

1. Java-Mindmap

Bringt Sie dazu, Java NIO vollständig zu beherrschen (Zusammenfassungsfreigabe)

2. Welcher Kanal ist die Essenz des zur Datenverarbeitung verwendeten I/O-Modells? Senden und Empfangen bestimmen maßgeblich die Leistung der Programmkommunikation.

Java unterstützt insgesamt drei Netzwerkprogrammierungsmodelle: BIO, NIO, AIO muss einen Thread zur Verarbeitung starten.


    NIO: Synchron und nicht blockierend. Der Serverimplementierungsmodus sieht vor, dass ein Thread mehrere Anforderungsverbindungen verarbeitet. Das heißt, alle vom Client gesendeten Anforderungen werden auf dem Multiplexer registriert und der Multiplexer fragt ab, bis die Verbindung hergestellt ist I/O-O-Anfrage wird verarbeitet.
  • AIO: Asynchrones, nicht blockierendes AIO führt das Konzept des asynchronen Kanals ein, übernimmt den Proactor-Modus, vereinfacht das Schreiben von Programmen und startet den Thread erst nach einer gültigen Anforderung. Seine Besonderheit besteht darin, dass das Betriebssystem ihn zuerst abschließt und dann benachrichtigt Der Server.
  • 3. BIO-, NIO-, AIO-Anwendungsszenarien
  • Die BIO-Methode eignet sich für Architekturen mit einer relativ kleinen und festen Anzahl von Verbindungen. Diese Methode stellt relativ hohe Anforderungen an Serverressourcen und die Parallelität ist auf Anwendungen beschränkt. Vor JDK1.4 Die einzige Option, aber das Verfahren ist einfach und leicht zu verstehen.

    Die NIO-Methode eignet sich für Architekturen mit einer großen Anzahl von Verbindungen und relativ kurzen Verbindungen (leichte Operationen), wie z. B. Chat-Server, Sperrsysteme, Kommunikation zwischen Servern usw. Die Programmierung ist komplizierter und JDK1.4 beginnt, sie zu unterstützen.
  • Die AIO-Methode wird in Architekturen mit einer großen Anzahl von Verbindungen und relativ langen Verbindungen (schweren Vorgängen) verwendet, z. B. bei Fotoalbum-Servern. Sie ruft das Betriebssystem vollständig auf, um an gleichzeitigen Vorgängen teilzunehmen zur Unterstützung
  • 4. Einfacher Prozess der BIO-Programmierung
  • Der Server startet einen ServerSocket;

    Der Client startet den Socket, um mit dem Server zu kommunizieren
  • Nachdem der Client eine Anfrage gesendet hat, konsultieren Sie zunächst den Server, um zu sehen, ob eine Thread-Antwort vorliegt.
  • Wenn eine Antwort vorliegt, Der Client-Thread wartet auf das Ende der Anforderung, bevor er mit der Ausführung fortfährt.
  • 5. NIO-Kern
  • NIO Es gibt drei Kernteile: Selektor, Kanal und Puffer.

    NIO ist pufferorientiert, bzw. blockorientiert. Die Daten werden in einen Puffer eingelesen und können bei Bedarf im Puffer hin und her verschoben werden , können Sie ein nicht blockierendes, hoch skalierbares Netzwerk bereitstellen.
  • HTTP2.0 verwendet Multiplexing-Technologie, um zu ermöglichen, dass dieselbe Verbindung mehrere Anforderungen gleichzeitig verarbeitet, und die Anzahl gleichzeitiger Anforderungen ist um mehrere Größenordnungen höher als bei HTTP1.1.
Kurz gesagt: NIO kann mehrere Anfragen mit einem Thread bearbeiten.

6. Vergleich zwischen BIO und NIO




BIO verarbeitet Daten in einem Stream, während NIO Daten in einem Block verarbeitet. Die Effizienz von Block-I/O ist viel höher als die von Stream-I/O BIO blockiert Ja, NIO ist nicht blockierend;

    BIO arbeitet basierend auf Byte-Stream und Zeichen-Stream, während NIO basierend auf Kanal und Puffer arbeitet und Daten immer vom Kanal in den Puffer gelesen oder daraus geschrieben werden den Puffer zum Kanal. Der Selektor wird zum Überwachen mehrerer Kanalereignisse (z. B. Verbindungsanforderungen, Datenankunft usw.) verwendet, sodass ein einzelner Thread mehrere Clientkanäle überwachen kann.
  • 7. Diagramm der drei Kernprinzipien von NIO
  • Beschreibung des Flussdiagramms:
  • Selektor entspricht einem Thread und ein Thread entspricht mehreren Kanälen (Verbindungen); Es gibt drei Kanäle, die sich im Selektor //Programm registrieren.

Jeder Kanal entspricht einem Puffer. Auf welchen Kanal das Programm umschaltet, ist ein wichtiges Konzept Der Selektor reagiert auf unterschiedliche Ereignisse, die auf jedem Kanal umgeschaltet werden. Bringt Sie dazu, Java NIO vollständig zu beherrschen (Zusammenfassungsfreigabe)

    Puffer ist ein Speicherblock, und auf der untersten Ebene befindet sich ein Array BIO. BIO ist entweder ein Eingabestream, der nicht bidirektional sein kann, aber der Puffer von NIO kann gelesen oder geschrieben werden und erfordert eine Flip-Methode
  • Kanal ist bidirektional und kann den Status des zugrunde liegenden Betriebssystems zurückgeben.

8. Puffer

Ein Puffer ist im Wesentlichen ein Puffer, der gelesen werden kann Der geschriebene Speicherblock kann als Containerobjekt (einschließlich eines Arrays) verstanden werden. Dieses Objekt bietet eine Reihe von Methoden, um die Verwendung des Speicherblocks zu erleichtern. Das Pufferobjekt verfügt über einige integrierte Mechanismen, die es verfolgen und aufzeichnen können Der Status des Puffers ändert sich. Der Kanal stellt einen Kanal zum Lesen von Daten aus Dateien und Netzwerken bereit, die gelesenen oder geschriebenen Daten müssen jedoch über den Puffer erfolgen.
In NIO ist Buffer eine übergeordnete Klasse der obersten Ebene, bei der es sich um eine abstrakte Klasse handelt.

1. Liste häufig verwendeter Puffer-Unterklassen

  • ByteBuffer, speichert Byte-Daten im Puffer;

  • ShortBuffer, speichert Zeichendaten im Puffer Area;

  • IntBuffer, speichert Ganzzahldaten im Puffer;

  • DoubleBuffer, speichert Dezimalzahlen im Puffer; s zu der Puffer;

  • 2. Vier Hauptattribute des Puffers
  • Markierung: Markierung
  • Position: der Index des nächsten Elements, das jedes Mal gelesen oder geschrieben werden soll lesen oder schreiben Der Wert wird jedes Mal geändert, um ihn auf das nächste Lesen und Schreiben vorzubereiten.

Limit: Gibt den aktuellen Endpunkt des Puffers an. Lese- und Schreibvorgänge können nicht an Positionen ausgeführt werden, an denen der Puffer das Limit überschreitet. Und das Limit kann geändert werden

  • Kapazität: Kapazität, also die maximale Datenmenge, die aufgenommen werden kann; sie wird beim Erstellen des Puffers festgelegt und kann nicht geändert werden.

  • 3. Die API, die häufig verwendet wird, ist die API. JDK1.4. Public Final Int Capacity () // Gibt die Kapazität dieses Puffers zurück. Public Final Int Position () //Die Position dieses Puffers zurückgeben

  • public final Buffer position (int newPositio)//Die Position dieses Puffers festlegen
  • public final int limit()//Die Grenze dieses Puffers zurückgeben

  • public final Buffer limit (int newLimit )//Legen Sie die Grenze dieses Puffers fest

public final Buffer mark( )//Setzen Sie eine Markierung an der Position dieses Puffers Bringt Sie dazu, Java NIO vollständig zu beherrschen (Zusammenfassungsfreigabe)

public final Buffer reset( )//Setzen Sie die Position dieses Puffers auf die zuvor markierte Position zurück

public final Buffer clear( )//Löschen Sie diesen Puffer, d. h. stellen Sie jede Markierung in ihren ursprünglichen Zustand zurück, aber die Daten werden nicht wirklich gelöscht, und nachfolgende Vorgänge werden sie überschreiben

public final Buffer flip( )//Invertieren this buffer

public final Buffer rewind( )//Diesen Puffer zurückspulen
  • public final int verbleibend( )//Gibt die Anzahl der Elemente zwischen der aktuellen Position und dem Limit zurück
  • public final boolean hasRemaining( )//Informiere den Strom Position und Ob es Elemente zwischen Einschränkungen gibt
  • public abstract boolean isReadOnly();// Informieren Sie, ob dieser Puffer ein schreibgeschützter Puffer ist
  • API eingeführt in JDK1.6
  • public abstract boolean hasArray();// Informieren Sie, ob dieser Puffer über ein zugängliches zugrunde liegendes Implementierungsarray verfügt
  • public abstract Object array();//Gibt das zugrundeliegende Implementierungsarray dieses Puffers zurück
  • public abstract int arrayOffset();//Gibt das zugrundeliegende Implementierungsarray dieses Puffers zurück Der Offset des ersten Pufferelements
  • public abstract boolean isDirect();//Informieren, ob dieser Puffer ein direkter Puffer ist
  • 9. Grundlegende Einführung
  • (1) NIO Kanäle ähneln Streams. Kanäle können gleichzeitig lesen und schreiben, während Streams nur lesen oder schreiben können. Kanäle können Daten asynchron lesen und schreiben. Kanäle können Daten aus Puffern lesen und in sie schreiben der Puffer

(2) Der Stream in BIO ist unidirektional. Beispielsweise kann das FileInputStream-Objekt nur Daten lesen, während der Kanal in NIO bidirektional ist und Lese- oder Schreibvorgänge ausführen kann.
    (3) Channel ist eine Schnittstelle in NIO
  • (4) Häufig verwendete Channel-Klassen sind: FileChannel, DatagramChannel, ServerSocketChannel und SocketChannel. ServerSocketChanne ähnelt ServerSocket und SocketChannel ähnelt Socket.
  • (5) FileChannel wird zum Lesen und Schreiben von Dateidaten verwendet, DatagramChannel wird zum Lesen und Schreiben von UDP-Daten verwendet, ServerSocketChannel und SocketChannel werden zum Lesen und Schreiben von TCP-Daten verwendet.
  • 2. FileChannel
  • FileChannel wird hauptsächlich zum Ausführen von E/A-Vorgängen für lokale Dateien verwendet:

Bringt Sie dazu, Java NIO vollständig zu beherrschen (Zusammenfassungsfreigabe)

lesen, Daten aus dem Kanal lesen und in den Puffer legen

schreiben Puffer Die Daten im Bereich werden in den Kanal geschrieben

transferFrom, kopieren Sie die Daten vom Zielkanal auf den aktuellen Kanal
  • transferTo, kopieren Sie die Daten vom aktuellen Kanal auf den Zielkanal

3. Hinweise und Details zu Puffer und Kanal

  • ByteBuffer unterstützt typisiertes Put und Get, zum Herausnehmen sollte der entsprechende Datentyp verwendet werden. Andernfalls kann es zu einer BufferUnderflowException kommen Ausnahme.

  • Sie können einen normalen Puffer in einen schreibgeschützten Puffer umwandeln.

  • NIO bietet auch MappedByteBuffer, mit dem Dateien direkt im Speicher (Speicher außerhalb des Heaps) geändert werden können. Die Synchronisierung mit Dateien wird von NIO abgeschlossen.

  • NIO unterstützt auch Lese- und Schreibvorgänge über mehrere Puffer (d. h. Pufferarrays), nämlich Scattering und Gathering.

10. Selektor(Selektor)

1. Grundlegende Einführung

  • NIO von Java unter Verwendung der nicht blockierenden IO-Methode. Sie können einen Thread verwenden, um mehrere Clientverbindungen zu verarbeiten, und Sie verwenden den Selector.

  • Selector kann erkennen, ob ein Ereignis auf mehreren registrierten Kanälen auftritt. Wenn ein Ereignis auftritt, ruft es das Ereignis ab und verarbeitet jedes Ereignis entsprechend. Auf diese Weise kann nur ein einziger Thread zur Verwaltung mehrerer Kanäle, also zur Verwaltung mehrerer Verbindungen und Anfragen, verwendet werden.

  • Nur wenn die Verbindung/der Kanal tatsächlich ein Lese- oder Schreibereignis hat, werden Lese- und Schreibvorgänge ausgeführt, wodurch der Systemaufwand erheblich reduziert wird und nicht für jede Verbindung ein Thread erstellt und mehrere Threads verwaltet werden müssen.

  • Vermeiden Sie den Overhead, der durch den Kontextwechsel zwischen mehreren Threads entsteht. ?? , fügen Sie den entsprechenden SelectionKey zur internen Sammlung hinzu und geben Sie ihn zurück. Die Parameter werden verwendet, um das Timeout festzulegen

selectedKeys(); //Alle SelectionKeys aus der internen Sammlung abrufen.

  • 3. Hinweise

  • Die ServerSocketChannel-Funktion in NIO ähnelt ServerSocket und die SocketChannel-Funktion ähnelt Socket.
  • 11. Einfache Server-Client-Kommunikation über NIO

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 Empfohlene Studie: „Java-Tutorial

    Das obige ist der detaillierte Inhalt vonBringt Sie dazu, Java NIO vollständig zu beherrschen (Zusammenfassungsfreigabe). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen