1. Introducción a la Biblioteca NIO
1. Búfer búfer
Un búfer es un objeto que contiene algunos datos que se escribirán y leen.
En NIO, todos los datos se procesan en un búfer. Al leer datos, se lee directamente del canal al búfer, y al escribir datos, también se escribe desde el búfer al canal.
Un búfer es esencialmente una matriz, generalmente una matriz de bytes (Bytebuffer) u otros tipos de matrices. Además, el búfer también proporciona información como el acceso estructurado a los datos y el mantenimiento de ubicaciones de lectura y escritura.
La relación de herencia de la clase de amortiguación se muestra en la figura a continuación:
2. Canal
El canal es un canal donde los datos de la red se leen y escriben a través del canal. La diferencia entre un canal y una transmisión es que el canal es bidireccional (el canal se puede usar para leer y escribir este último al mismo tiempo), y la transmisión solo se mueve en una dirección.
Los canales se pueden dividir aproximadamente en dos categorías: selectableChannel para la lectura y la escritura de la red (ServerSocketchannel y Socketchannel son sus subclases) y fileChannel para operaciones de archivos.
El siguiente ejemplo muestra el archivo que usa Filechannel para escribir datos en un archivo, leer datos de un archivo y copiar los datos del archivo a otro archivo:
public class niotest {public static void main (string [] args) lanza ioexception {copyFile (); } // Copiar el archivo privado void void copyfile () {fileInputStream in = null; FileOutputStream out = null; intente {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.allocate (1024); int bytesread = Inchannel.read (buffer); while (bytesread! =-1) {buffer.flip (); Outchannel.write (búfer); buffer.clear (); bytesread = Inchannel.read (buffer); }} Catch (FileNotFoundException e) {// TODO BLOQUE DE CABTA AUTO GENERADA E.PRINTSTACKTRACE (); } Catch (ioException e) {// tODO Auto Generated BLOCK E.PrintStackTRace (); }} // Escribir archivo privado void void writeFilenio () {try {randomAccessFile fout = new RandomAccessFile ("src/main/java/data/nio-data.txt", "rw"); FileChannel fc = fout.getChannel (); Bytebuffer buffer = bytebuffer.allocate (1024); buffer.put ("Hi123" .getBytes ()); buffer.flip (); intente {fc.write (buffer); } Catch (ioException e) {// tODO Auto Generated BLOCK E.PrintStackTRace (); }} Catch (FileNotFoundException e) {// TODO BLOQUE DE CABTA AUTO GENERADA E.PRINTSTACKTRACE (); }} // Leer archivo privado void void readFilenio () {fileInputStream FileInputStream; Pruebe {fileInputStream = new FileInputStream ("src/main/java/data/nio-data.txt"); FileChannel fileChannel = fileInputStream.getChannel (); // Obtenga el canal de fileInputStream byteBuffer byteBuffer = byteBuffer.allocate (1024); // Cree un buffer int bytesread = fileChannel.read (bytebufferer); // Leer los datos en el buffer while (bytesread! =-1) {/ * limit. */ bytebuffer.flip (); // hasremining (): Informe si hay un elemento entre la posición actual y el límite mientras (bytebuffer.hasremaining ()) {system.out.print ((char) bytebuffer.get ()); } /** Borrar el búfer* posición = 0; * límite = capacidad; */ bytebuffer.clear (); bytesread = fileChannel.read (bytebuffer); }} Catch (FileNotFoundException e) {// TODO BLOQUE DE CABTA AUTO GENERADA E.PRINTSTACKTRACE (); } Catch (ioException e) {// tODO Auto Generated BLOCK E.PrintStackTRace (); }}}3. Selector de multiplexor
El multiplexor proporciona la capacidad de seleccionar tareas que estén listas. El selector encuestará constantemente el canal registrado en él. Si un canal envía un evento de lectura o escritura, el canal estará en el estado listo y será encuestado por el selector. Luego, el conjunto de canales listos se puede obtener a través de la tecla de selección para realizar operaciones de E/S posteriores.
Un selector de multiplexor puede encuestar múltiples canales al mismo tiempo. Dado que JDK usa EPOLL en lugar de la implementación seleccionada tradicional, no tiene el límite de la manejo de conexión máxima 1024/2048, lo que significa que solo se requiere que un hilo sea responsable de la encuesta del selector y puede acceder a miles de clientes. El modelo se muestra en la figura a continuación:
Procese un selector con un solo hilo. Para usar Selector, debe registrar el canal con Selector y luego llamar a su método select (). Este método bloqueará hasta que un canal registrado tenga el evento listo. Una vez que este método regresa, el hilo puede procesar estos eventos, como nuevas conexiones, recepción de datos, etc.
Nota:
1. ¿Qué modelo seleccionado?
Select es un mecanismo de activación de eventos, que desencadena el procesamiento cuando se produce el evento de espera, y se utiliza principalmente para el procesamiento del cliente por la implementación de los servidores Linux.
Puede detectar simultáneamente un conjunto de dispositivos IO sin bloqueo que admiten el no bloqueo, ya sea que haya eventos (como salida de error de alta prioridad legible, de alta prioridad, etc.) hasta que un dispositivo desencadena un evento o excede el tiempo de espera especificado. Es decir, su responsabilidad no es hacer IO, sino ayudar a la persona que llama a encontrar el dispositivo actualmente listo.
2. ¿Cuál es el modelo Epoll?
La idea de diseño de EPOLL es dividir la operación única de Select/Poll en 1 EPOLL_CREATE + Múltiples EPOLL_CTRL + One Wait. Además, el kernel ha agregado un sistema de archivos "EventPollfs" para operaciones EPOLL. Cada o más descriptores de archivos a ser monitoreados tienen un nodo inodo del sistema de archivos EventPollfs correspondiente, y la información principal se almacena en la estructura EventPoll. La información importante de los archivos monitoreados se almacena en la estructura de epítem. Entonces son una relación de uno a muchos.
2. Desarrollo del lado del servidor NIO
Descripción de la función: Encienda el lado del servidor y envíe una cadena de hola a cada cliente de acceso.
Hay varios pasos principales para usar NIO para el desarrollo del lado del servidor:
1. Cree Serversocketchannel y configúrelo en modo sin bloqueo
ServerSocketchannel = ServerSocketchannel.open (); ServerSocketchannel.ConfigureBlocking (falso);
2. Binde la audición y configuración de los parámetros TCP, como el tamaño de la cartera
ServerSocketchannel.socket (). Bind (nuevo inetSocketAddress (8080));
3. Cree un hilo de E/S independiente para sondear el selector de multiplexor
4. Cree un selector, registre el ServidorCetchEnchannel que creó anteriormente en el selector y escuche SelectionKey.accept
selector = selector.open (); ServerSocketchannel.register (selector, selectionKey.op_accept);
5. Inicie el hilo de E/S, ejecute el método Selector.Select () en el cuerpo del bucle y encueste el canal listo
mientras (true) {try {// select () bloquea hasta que al menos un canal esté listo en el evento que se registró // Si no hay canal listo, bloqueará aquí // select (tiempo de espera largo) es lo mismo que select (), excepto que bloqueará el tiempo de espera de los milisegundos (parámetros). selector.select (); } Catch (ioException e) {// tODO Auto Generated BLOCK E.PrintStackTRace (); romper; }}6. Al sondear el canal en el estado listo, debe ser juzgado. Si es el estado Op_accept, significa que es un nuevo acceso al cliente. Luego llame al método Serversocketchannel.accept () para aceptar el nuevo cliente.
// Devuelve la selección de listos y luego iterar para ejecutar set <selectionKey> readkeys = selector.selectedKeys (); for (iterator <selectionKey> it = readkeys.iterator (); it.hasnext ();) {selectionKey key = it.next (); it.remove (); Pruebe {if (key.isaceptable ()) {ServerSocketchannel Server = (ServerSocketchannel) key.channel (); Socketchannel Client = server.accept (); Client.ConfigureBlocking (falso); Client.register (selector, selectionKey.op_write); } else if (key.iswritable ()) {Socketchannel Client = (Socketchannel) key.channel (); Bytebuffer buffer = bytebuffer.allocate (20); Cadena str = "hola"; buffer = bytebuffer.wrap (str.getbytes ()); Client.write (buffer); key.cancel (); }} catch (ioException e) {E.PrintStackTrace (); key.cancel (); intente {key.channel (). Close (); } Catch (ioException e1) {// TODO Auto Generado Bloque E1.PrintStackTrace (); }}}7. Establezca el enlace del cliente recientemente accedido Socketchannel en modo sin bloqueo, y configure algunos otros parámetros TCP.
if (key.isaceptable ()) {ServerSocketchannel Server = (ServerSocketchannel) key.channel (); Socketchannel Client = server.accept (); Client.ConfigureBlocking (falso); ...}8. Registre Socketchannel a Selector y escuche OP_Write
Client.register (selector, selectionKey.op_write);
9. Si el canal encuestado es OP_Write, significa que los datos se deben escribir en el SockChannel, entonces el objeto ByteBuffer se construye y se escribe el paquete de datos.
else if (key.iswritable ()) {Socketchannel Client = (Socketchannel) key.channel (); Bytebuffer buffer = bytebuffer.allocate (20); Cadena str = "hola"; buffer = bytebuffer.wrap (str.getbytes ()); Client.write (buffer); key.cancel (); }El código completo es el siguiente:
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.selector; import java.nio.channels.serversock; java.nio.channels.socketchannel; import java.util.iterator; import java.util.set; public class Serversocketchetchanneldemo {public static void main (string [] args) {Serversocketchetchannel ServerSockEnchannel; selector selector = null; intit {Serversocketchetchannel = ServerSocketchannel.open (); Serversocketchannel.ConfigureBlowing (false); Serversocketchannel.socket (). Bind (nuevo inetSocketAddress (8080)); selector = selector.open (); ServersocketchEnchannel. blocke.printstackTrace ();} while (true) {try {// select () bloques hasta que al menos un canal esté listo en el evento que se registra // Si no hay ningún canal listo, bloqueará aquí todo el tiempo.//select(long Timeout) es lo mismo que Select (), excepto que bloqueará el tiempo de tiempo de tiempo (parámetros). selector.select ();} catch (ioException e) {// tODO Generado automático BLOCKE.PRINTSTACKTRACE (); break;} // Devuelve el listado de selección y luego iterar para ejecutar set <selectionKey> readkeys = selector.selectedKeyS (); para (iterator <selectionKey> it = readkeS.Iterator (); it. it. ();););););););););););); {SelectionKey key = it.next (); it.remove (); try {if (key.isaceptable ()) {Serversocketchannel servidor = (ServerSocketchannel) key.channel (); socketchannel cliente = server.accept (); client.configureBlocking (false); client.ripister (selector, selectionKey.write);};} if (key.iswritable ()) {Socketchannel Client = (Socketchannel) key.channel (); byteBuffer buffer = byteBuffer.allocate (20); string str = "hello"; buffer = byteBuffer.wrap (str.getByTes ()); Client.write (buffer); key.cancel ();}}} Catch (io) {E.PrintStackTrace (); key.cancel (); try {Key.channel (). Close ();} Catch (ioException e1) {// tODO Autorgenerado BLOCKE1.PRINTSTACKTRACE ();}}}}}}}}}}}}}}}}}Utilizamos Telnet Localhost 8080 para simular varios clientes:
Los resultados de la ejecución del programa son los siguientes:
Resumir
Lo anterior es toda la explicación detallada del desarrollo del lado del servidor Java Nio en este artículo, espero que sea útil para todos. Los amigos interesados pueden continuar referiéndose a otros temas relacionados en este sitio. Si hay alguna deficiencia, deje un mensaje para señalarlo. ¡Gracias amigos por su apoyo para este sitio!