1. Información general
En la API BIO, la entrada y la salida se realizan a través de dos flujos, InputStream y outPutStream. NIO utiliza un canal de comunicación bidireccional en lugar de ellos. El canal (Channel) debe depender del búfer (búfer) para lograr la comunicación.
El flujo de comparación de la canalización tiene más funciones, como no bloqueo, asignación de memoria fuera del montón y copia cero.
2. Definición de búfer
La canalización depende del búfer, así que primero introduzcamos el concepto y el uso del búfer.
Se mantiene una matriz dentro del búfer para almacenar datos. El búfer no admite ningún tipo de datos almacenados, solo se pueden almacenar algunos tipos de datos básicos.
Tiene las funciones de leer y escribir y vaciar. Sin seguridad de subprocesos, debe controlar la seguridad de subprocesos usted mismo.
Ofrezca a todos un beneficio para recibir un mapa de habilidades de arquitectura Java gratuito. Tenga en cuenta que es gratis
、
Gratis para recibir el + V requerido para recibir
3. Estructura interna del amortiguador
Tome ByteBuffer, por ejemplo, es una subclase de Buffer, que se utiliza para almacenar datos de tipo Byte, otros búferes son similares, pero los tipos de datos almacenados son diferentes.
Una matriz de bytes se mantiene internamente para almacenar datos.
El búfer de la clase padre tiene 4 atributos importantes:
-
capacidad
Indica el tamaño de la matriz.
-
límite
Limite el rango de lectura y escritura del búfer, el valor predeterminado es igual a la capacidad
-
posición
La posición actual de lectura y escritura, el valor predeterminado es 0, cada vez que se lee un bit o se escribe un bit, +1.
-
marca
Haga una marca de reinicio para establecer la posición en esta posición. El valor predeterminado es -1
La relación de posición de los cuatro: marca <= posición <= límite <= capacidad
4. Almacenar operaciones de uso común
4.1 asignar
Asigne el espacio de almacenamiento del búfer e inicialice todos los atributos. P.ej:
Los resultados obtenidos son los siguientes:
4.2 envoltura
Empaquetar un búfer basado en una matriz, la posición es 0, el límite es el valor de capacidad
El código de prueba es el siguiente:
Los resultados del búfer son los siguientes:
4.3 poner
Escriba en el búfer, el código de prueba es el siguiente:
En el proceso de put, se cambiará la posición, es decir, cada vez que se escriba un número, la posición será +1. Cuando se escriba la séptima, la posición superará el límite límite, y en este momento se lanzará una excepción BufferOverflowExeception
4.4 voltear
Prepárese para la lectura. Después de poner, ejecute el giro para restablecer la posición y ajústelo a 0.
La siguiente operación de lectura comienza desde la posición.
El código de prueba es el siguiente:
El resultado del búfer después de que se ejecuta la colocación 5 es el siguiente:
Luego ejecute flip, los resultados son los siguientes:
De acuerdo con los resultados, puede ver el rol de flip, establecer la posición en 0
Y el límite se establece en la posición original, el límite en este momento limita el alcance de la lectura.
4.5 obtener
Lea el contenido del búfer, cada vez que lea uno, mueva una posición después de la posición
El código de prueba es el siguiente:
Cuando se lee el quinto, el contenido del búfer en este momento es el siguiente: Se
puede encontrar que la posición en este momento ha alcanzado el límite del límite. Si lo vuelve a leer, informará un error, lea la excepción fuera de los límites BufferUnderflowException
4.6 marca
Establecer el bit de marca y registrar la posición actual
El código de prueba es el siguiente:
Después de ejecutar la marca, establezca la propiedad de la marca en posición, es decir, haga una marca
4.7 reiniciar
La función de reinicio es establecer el valor de la posición a marcar para modificar un dato en el búfer. O leer un dato en el búfer repetidamente.
El código de prueba es el siguiente:
Después de ejecutar el reinicio, los resultados del búfer son los siguientes
Después de grabar la marca de la marca, lee dos números y modifica los dos números. Después del reinicio, la posición vuelve a la posición registrada por la marca. En este momento, si escribe de nuevo, puede modificar los dos números en el búfer. El resultado es el siguiente
4.8 claro
Restablece los atributos del búfer, pero no borrará los datos en el búfer
Establezca la posición de la posición en 0, la marca en -1. El límite se establece en la capacidad
El código de prueba es el siguiente:
Después de ejecutar clear, el resultado del búfer es el siguiente:
4.9 rebobinar
Prepárese para volver a leer. Establezca la posición en 0 y el límite sin cambios. la marca se establece en -1
El código de prueba es el siguiente: Después de
ejecutar el rebobinado, los resultados del búfer son los siguientes:
4.10 restantes
Cuántos rangos legibles se devuelven
Cuál es posición límite
El código de prueba es el siguiente:
En este momento, el límite es igual a 5. Después de leer dos veces, se ejecuta el método restante y el resultado devuelto es 3, lo que indica el contenido legible restante
5. chanel
Las tuberías se utilizan para conectar archivos, enchufes de red, etc. Puede realizar lectura y escritura de dos operaciones de E / S al mismo tiempo. Se llama tubería de dos vías. Tiene dos estados: conectado y cerrado. Está abierto cuando se crea la tubería. Una vez cerrado, informará cuando se llame a la operación de E / S. ClosedChannelException
. Se isOpen
puede juzgar si está en estado abierto mediante el método de canalización .
5.1 Canalización de archivos FileChannel
El nombre sólido sugiere que se utiliza para manipular archivos. Además de las operaciones normales, también admite las siguientes funciones:
-
Soporte para leer y escribir en el área especificada del archivo
-
Mapeo de memoria fuera del montón, al leer y escribir archivos grandes, se puede mapear directamente desde la memoria declarada de JVM para mejorar la eficiencia de lectura y escritura.
-
La tecnología de copia cero, a través de
transferFrom
otransferTo
directamente la transmisión de datos a un canal, mejora considerablemente el rendimiento. -
Bloquear el área designada del archivo para evitar que otros programadores accedan a él
Open FileChannel solo se puede abrir a través del flujo en la actualidad, como inputStream.getChannel () y outputStream.getChannel (), solo se puede acceder a la canalización abierta a través del flujo de entrada y solo se puede escribir el outputStream abierto. De lo contrario, NonWritableChannelException y NonReadableChannelException se lanzarán por separado.
Si desea que la tubería admita tanto lectura como escritura, debe usar el modo de RandomAccessFile
lectura y escritura.
El siguiente código de prueba es el uso básico de la canalización de archivos
//1. 打开文件管道
FileChannel channel = new RandomAccessFile(file_name,"rw").getChannel();
ByteBuffer buffer=ByteBuffer.allocate(1024); // 声明1024个空间
// 从文件中 读取数据并写入管道 再写入缓冲 channel.read(buffer); buffer.flip();//上面学的,写完之后,需要将position归0,为了后面的读取做准备 byte[] bytes= new byte[buffer.remaining()]; int i =0; while (buffer.hasRemaining()){ bytes[i++]= buffer.get(); } System.out.println(new String(bytes)); // 把缓冲区数据写入到管道 channel.write(ByteBuffer.wrap("森林大帅哥".getBytes())); channel.close();
5.2 Tubería de conexión UDP DatagramChannel
UDP es un protocolo sin conexión y DatagramChannel proporciona servicios para que este protocolo reciba mensajes de los clientes.
El uso básico de DatagramChannel es el siguiente:
public void test1() throws IOException { DatagramChannel channel=DatagramChannel.open(); // 绑定端口 channel.bind(new InetSocketAddress(8080)); ByteBuffer buffer=ByteBuffer.allocate(8192); while (true){ buffer.clear(); // 清空还原 channel.receive(buffer); // 阻塞 buffer.flip(); byte[] bytes=new byte[buffer.remaining()]; buffer.get(bytes); System.out.println(new String(bytes)); } }
Utilice el comando nc-uv 127.0.0.1 8080 para enviar udp al número de puerto IP especificado
La consola de ideas generará el siguiente resultado:
5.3 Tubería de conexión TCP
TCP es un protocolo de conexión y la comunicación solo se puede realizar una vez establecida la conexión. Esto requiere las siguientes dos canalizaciones:
-
** ServerSocketChannel: ** Se utiliza para establecer una conexión con el cliente.
-
** SocketChannel: ** Se usa para leer y escribir mensajes con el cliente
El código de prueba es el siguiente:
@Test
public void test1() throws IOException { // 用于与客户端建立连接 ServerSocketChannel channel = ServerSocketChannel.open(); channel.bind(new InetSocketAddress(8080)); while (true) {//循环接收请求,分配子线程去执行请求 // 用于和客户端进行消息读写 SocketChannel socketChannel = channel.accept(); handle(socketChannel); } } public void handle(final SocketChannel socketChannel) throws IOException { // 2.通信 Thread thread = new Thread(new Runnable() { @Override public void run() { ByteBuffer buffer = ByteBuffer.allocate(8192); while (true) { try { buffer.clear(); socketChannel.read(buffer); // 从buffer 当中读出来 buffer.flip(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); String message = new String(bytes); System.out.println(message); // 写回去 buffer.rewind(); socketChannel.write(buffer); if (message.trim().equals("exit")) { break; } } catch (Exception e) { e.printStackTrace(); } } try { socketChannel.close(); } catch (IOException e) { e.printStackTrace(); } } }); thread.start(); }
Se puede probar con el comando TCP service telnet 127.0.0.1 8080