Java unterstützt drei Netzwerkprogrammierungsmodelle: BIO, NIO, AIO
Nachdem der Client eine Anfrage gesendet hat, konsultiert er zunächst den Server, um festzustellen, ob eine Thread-Antwort vorliegt abgelehnt;
Wenn eine Antwort vorliegt, wartet der Client auf das Ende der Anforderung, bevor er mit der Ausführung fortfährt. .
NIO ist eine pufferorientierte oder blockorientierte Programmierung. Die Daten werden in einen Puffer eingelesen und können bei Bedarf im Puffer hin und her verschoben werden, was die Flexibilität im Verarbeitungsprozess erhöht . Verwenden Sie es, um ein nicht blockierendes, hoch skalierbares Netzwerk bereitzustellen.
7. Schematische Darstellung der drei Kernprinzipien von NIO
Daten werden über den Puffer gelesen und geschrieben. this und BIO , BIO ist entweder ein Eingabestream oder ein Ausgabestream, der nicht bidirektional sein kann, aber der Puffer von NIO kann gelesen oder geschrieben werden und erfordert einen Flip-Methodenwechsel; #channel ist bidirektional, Sie können den Status des zugrunde liegenden Betriebssystems zurückgeben, z. B. Linux, der zugrunde liegende Betriebssystemkanal ist bidirektional
;
Ein Puffer ist im Wesentlichen ein Speicherblock, der Daten lesen und schreiben kann. Er kann als Containerobjekt (einschließlich eines Arrays) verstanden werden. Dieses Objekt stellt eine Reihe von Methoden bereit, die die Verwendung erleichtern können Speicherblöcke und Pufferobjekte verfügen über integrierte Mechanismen, die Änderungen des Pufferzustands verfolgen und aufzeichnen können. 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.
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;
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 wurde eingeführt, als die häufig verwendete Puffer-API
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
public final Buffer reset( ) // Setzen Sie die Position dieses Puffers auf die Position der vorherigen Markierung zurück
public final Buffer clear( )//Löschen Sie diesen Puffer, d. h. stellen Sie jede Markierung auf ihren Anfangszustand wieder her, aber die Daten werden nicht wirklich gelöscht, und wird durch nachfolgende Operationen überschrieben
public final Buffer flip( )//Diesen Puffer umkehren
public final Buffer rewind( )//Diesen Puffer zurückspulen
public final int verbleibend( )//Zurückgeben die aktuelle Position mit Anzahl der Elemente zwischen den Grenzwerten
public final boolean hasRemaining( ) // Gibt an, ob Elemente zwischen der aktuellen Position und dem Grenzwert vorhanden sind
public abstract boolean isReadOnly( ); // Gibt an, ob dieser Puffer vorhanden ist ist ein schreibgeschützter Puffer
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 zugrunde liegende Implementierungsarray dieses Puffers zurück
public abstract int arrayOffset();//Gibt den Offset des ersten Pufferelements im zugrunde liegenden Implementierungsarray dieses Puffers zurück
public abstract boolean isDirect( ; Lesen und Das Schreiben kann gleichzeitig ausgeführt werden, während Streams nur lesen oder schreiben können.
Kanäle können Daten asynchron lesen und schreiben. Kanäle können Daten aus dem Puffer lesen oder in den Puffer schreiben. 2) Der Stream in BIO ist unidirektional. Beispielsweise kann das FileInputStream-Objekt nur Daten lesen, während der Kanal in NIO bidirektional ist und lesen oder schreiben kann.
(3) Channel ist eine Schnittstelle in NIOlesen, Daten aus dem Kanal lesen und in den Puffer legen
transferFrom, kopieren Sie die Daten vom Zielkanal auf den aktuellen Kanal
transferTo, kopieren Sie die Daten vom aktuellen Kanal auf den Zielkanal
ByteBuffer unterstützt typisiertes Put und Get, zum Abrufen sollte der entsprechende Datentyp verwendet werden. Andernfalls kann es zu einer BufferUnderflowException-Ausnahme kommen .
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.
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
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(); }}
Das obige ist der detaillierte Inhalt vonAnalysieren Sie die Verwendung und Eigenschaften von Java NIO-Beispielen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!