Série de explicação detalhada do princípio Netty (1) --- Buffer & Chanel em NIO

1. Visão Geral

Na API BIO, a entrada e a saída são realizadas por meio de dois fluxos, InputStream e outPutStream. NIO usa um canal de comunicação bidirecional em vez deles. O canal (Canal) deve contar com o buffer (Buffer) para alcançar a comunicação.

O fluxo de comparação do pipeline tem mais recursos, como mapeamento de memória fora do heap, sem bloqueio e cópia zero.

 

2. Definição de buffer

O pipeline depende do buffer, portanto, vamos primeiro apresentar o conceito e o uso do buffer.

Uma matriz é mantida dentro do buffer para armazenar dados. O buffer não suporta nenhum tipo de dado armazenado e só pode armazenar alguns tipos de dados básicos.

Tem as funções de leitura e escrita e esvaziamento. Sem a segurança da linha, você precisa controlar a segurança da linha por si mesmo.

Dê a todos o benefício de receber um mapa de habilidades de arquitetura Java gratuito. Observe que é gratuito 

Livre para receber o + V necessário para receber

3. Estrutura interna do buffer

Tome ByteBuffer como exemplo, é uma subclasse de Buffer, usada para armazenar dados do tipo Byte, outros buffers são semelhantes, mas os tipos de dados armazenados são diferentes.

Uma matriz de bytes é mantida internamente para armazenar dados.

A classe pai Buffer tem 4 atributos importantes:

  • capacidade

    Indica o tamanho da matriz

  • limite

    Limita o intervalo legível e gravável do Buffer, o padrão é igual à capacidade

  • posição

    A posição atual de leitura e gravação, o padrão é 0, toda vez que um bit é lido ou um bit é escrito, +1.

  • marca

    Faça uma marca para reiniciar para definir a posição para esta posição. O padrão é -1

A relação de posição dos quatro: marca <= posição <= limite <= capacidade

4. Operações de buffer comumente usadas

4.1 alocar

Aloque o espaço de armazenamento do buffer e inicialize todos os atributos. Por exemplo:

Insira a descrição da imagem aqui
Os resultados obtidos são os seguintes:
Insira a descrição da imagem aqui

4.2 embrulhar

Empacote um buffer com base em uma matriz, a posição é 0, o limite é o valor da capacidade

O código de teste é o seguinte:

Insira a descrição da imagem aqui
Os resultados do buffer são os seguintes:

Insira a descrição da imagem aqui

4,3 colocar

Grave no buffer, o código de teste é o seguinte:

Insira a descrição da imagem aqui

No processo de colocação, a posição será alterada, ou seja, toda vez que um número for escrito, a posição será +1. Na 7ª escrita, a posição excederá o limite, e uma exceção BufferOverflowExeception será lançada neste momento
Insira a descrição da imagem aqui

4,4 flip

Prepare-se para a leitura. Depois de colocar, execute o flip para redefinir a posição e defina-a para 0.

A operação de leitura subsequente começa na posição.

O código de teste é o seguinte:

Insira a descrição da imagem aqui
O resultado do buffer após a execução de put 5 é o seguinte:

Insira a descrição da imagem aqui
Em seguida, execute o flip, os resultados são os seguintes:
Insira a descrição da imagem aqui
De acordo com os resultados, você pode ver a função do flip, defina a posição como 0

E o limite é definido para a posição original, o limite neste momento limita o escopo da leitura.

4,5 obter

Leia o conteúdo do buffer, cada vez que você ler um, mova uma posição após a posição

O código de teste é o seguinte:

Insira a descrição da imagem aqui

Quando o quinto é lido, o conteúdo do buffer neste momento é o seguinte:
Insira a descrição da imagem aqui
Pode-se descobrir que a posição neste momento atingiu o limite do limite. Se você ler novamente, ele relatará um erro, exceção de leitura fora dos limites BufferUnderflowException
Insira a descrição da imagem aqui

Marca 4,6

Defina o bit de marca e registre a posição atual

O código de teste é o seguinte:

Insira a descrição da imagem aqui

Após executar a marca, defina a propriedade da marca para a posição, ou seja, faça uma marca
Insira a descrição da imagem aqui

4.7 reset

A função de redefinir é definir o valor da posição a ser marcada para modificar uma parte dos dados no buffer. Ou leia um pedaço de dados no buffer repetidamente.

O código de teste é o seguinte:
Insira a descrição da imagem aqui

Depois de executar o reset, os resultados do buffer são os seguintes

Insira a descrição da imagem aqui

Depois de registrar a marca, dois números foram lidos e os dois números foram modificados. Após a reinicialização, a posição retorna para a posição registrada pela marca. Nesse momento, se você escrever novamente, poderá modificar os dois números no buffer. O resultado é o seguinte

Insira a descrição da imagem aqui

4,8 claro

Reinicialize os atributos do buffer, mas não apagará os dados do buffer

Defina a posição da posição para 0, marque para -1. O limite é definido para a capacidade

O código de teste é o seguinte:

Insira a descrição da imagem aqui

Depois de executar a limpeza, o resultado do buffer é o seguinte:

Insira a descrição da imagem aqui

4,9 retroceder

Prepare-se para reler. Defina a posição como 0 e limite inalterado. a marca é definida como -1

O código de teste é o seguinte: Depois de
Insira a descrição da imagem aqui
executar o retrocesso, os resultados do buffer são os seguintes:
Insira a descrição da imagem aqui

4,10 restantes

Quantos intervalos legíveis são retornados

Qual é a posição limite

O código de teste é o seguinte:
Insira a descrição da imagem aqui
Neste momento, o limite é igual a 5. Depois de ler duas vezes, o método restante é executado e o resultado retornado é 3, indicando o conteúdo legível restante

5. chanel

Pipes são usados ​​para conectar arquivos, sockets de rede, etc. Ele pode ler e gravar duas operações de E / S ao mesmo tempo. É chamado de tubo bidirecional. Tem dois estados: conectado e fechado. Ele está aberto quando o tubo é criado. Depois de fechado, ele informa quando a operação de E / S é chamada. ClosedChannelException . isOpen Pode-se avaliar se está no estado aberto pelo método do pipeline .

5.1 Pipeline de arquivo FileChannel

O nome sólido sugere que ele é usado para manipular arquivos. Além das operações normais, ele também oferece suporte aos seguintes recursos:

  • Suporte para ler e gravar na área especificada do arquivo

  • Mapeamento de memória out-of-heap, ao ler e gravar arquivos grandes, pode ser mapeado diretamente da memória declarada da JVM para melhorar a eficiência de leitura e gravação.

  • Tecnologia Zero-cópia, através  transferFrom ou transferTo directamente a transmissão de dados para um canal, melhora muito o desempenho.

  • Bloqueie a área designada do arquivo para evitar que outros programadores o acessem

Abrir FileChannel só pode ser aberto por meio do fluxo no momento, como inputStream.getChannel () e outputStream.getChannel (), o pipeline aberto por meio do fluxo de entrada só pode ser acessado e o outputStream só pode ser aberto para gravação. Caso contrário, NonWritableChannelException e NonReadableChannelException serão lançados separadamente.

Se você deseja que o pipe suporte leitura e gravação, você deve usar o modo de RandomAccessFile leitura e gravação.

O código de teste a seguir é o uso básico do pipeline de arquivos

				//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 DatagramChannel socket UDP pipe

UDP é um protocolo sem conexão e DatagramChannel fornece serviços para este protocolo para receber mensagens de clientes.

O uso básico do DatagramChannel é o seguinte:

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)); } } 

 

Use o comando nc-uv 127.0.0.1 8080 para enviar udp para o número da porta IP especificada

Insira a descrição da imagem aqui
O console de ideias terá a seguinte saída:

Insira a descrição da imagem aqui

5.3 tubo de soquete TCP

TCP é um protocolo de conexão e a comunicação só pode ser feita após o estabelecimento de uma conexão. Isso requer os seguintes dois pipelines:

  • ** ServerSocketChannel: ** Usado para estabelecer uma conexão com o cliente

  • ** SocketChannel: ** Usado para ler e escrever mensagens com o cliente

O código de teste é o seguinte:

@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(); } 

 

Pode ser testado pelo comando de serviço TCP telnet 127.0.0.1 8080

Insira a descrição da imagem aqui

Acho que você gosta

Origin blog.csdn.net/yuandengta/article/details/109183903
Recomendado
Clasificación