1. Introdução à Nio Library
1. Buffer buffer
Um buffer é um objeto que contém alguns dados a serem escritos e lidos.
No NIO, todos os dados são processados em um buffer. Ao ler os dados, eles são lidos diretamente do canal para o buffer e, ao escrever dados, ele também é escrito no buffer para o canal.
Um buffer é essencialmente uma matriz, geralmente uma matriz de bytes (bytebuffer) ou outros tipos de matrizes. Além disso, o buffer também fornece informações como acesso estruturado a dados e manutenção de locais de leitura e gravação.
A relação de herança da classe buffer é mostrada na figura abaixo:
2. Canal
O canal é um canal em que os dados da rede são lidos e gravados através do canal. A diferença entre um canal e um fluxo é que o canal é bidirecional (o canal pode ser usado para ler e escrever o último ao mesmo tempo) e o fluxo se move apenas em uma direção.
Os canais podem ser divididos aproximadamente em duas categorias: SelectableChannel para leitura e escrita de rede (ServerSocketchAnnel e Socketchannel são suas subclasses) e o FileChannel para operações de arquivo.
O exemplo a seguir mostra o arquivo usando o FileChannel para gravar dados em um arquivo, ler dados de um arquivo e copiar os dados do arquivo para outro arquivo:
classe pública niotest {public static void main (string [] args) lança ioexception {copyfile (); } // copie o arquivo private static void copyfile () {fileInputStream em = null; FileOutputStream Out = NULL; tente {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 (); Buffer bytebuffer = bytebuffer.Allocate (1024); int bytesread = Inchannel.read (buffer); while (bytesread! =-1) {buffer.flip (); outchannel.write (buffer); buffer.clear (); bytesRead = Inchannel.read (buffer); }} catch (filenotfoundException e) {// TODO GATE GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); } catch (ioexception e) {// TODO GATO GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); }} // Escreva arquivo privado estático void writeFilenio () {try {aleatomAccessFile fout = new RandomAccessFile ("src/main/java/data/nio-data.txt", "rw"); Filechannel fc = fout.getChannel (); Buffer bytebuffer = bytebuffer.Allocate (1024); buffer.put ("hi123" .getBytes ()); buffer.flip (); tente {fc.write (buffer); } catch (ioexception e) {// TODO GATO GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); }} catch (filenotfoundException e) {// TODO GATE GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); }} // Leia o arquivo privado estático void readfilenio () {FileInputStream FileInputStream; tente {fileInputStream = new FileInputStream ("src/main/java/data/nio-data.txt"); Filechannel filechannel = fileInputStream.getChannel (); // Obtenha canal do FileInputStream bytebuffer bytebuffer = bytebuffer.allocate (1024); // cria um buffer int bytesread = filechannel.read (bytebuffer); // leia os dados para o tampão enquanto */ bytebuffer.flip (); // hasremaining (): informe se existe um elemento entre a posição atual e o limite enquanto (bytebuffer.hasReNaining ()) {System.out.print ((char) byteBuffer.get ()); } /** Limpe o buffer* posição = 0; * limite = capacidade; */ bytebuffer.clear (); bytesread = filechannel.read (bytebuffer); }} catch (filenotfoundException e) {// TODO GATE GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); } catch (ioexception e) {// TODO GATO GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); }}}3. Seletor de multiplexador
O multiplexador fornece a capacidade de selecionar tarefas prontas. O seletor pesquisará constantemente o canal registrado nele. Se um canal enviar um evento de leitura ou gravação, o canal estará no estado pronto e será pesquisado pelo seletor. Em seguida, o conjunto de canais prontos pode ser obtido através do SelectionKey para executar operações subsequentes de E/S.
Um seletor de multiplexador pode pesquisar vários canais ao mesmo tempo. Como o JDK usa o Epoll em vez da implementação de seleção tradicional, ele não possui o limite de manipulação máxima de conexão 1024/2048, o que significa que apenas um thread é necessário para ser responsável pela pesquisa do seletor e pode acessar milhares de clientes. O modelo é mostrado na figura abaixo:
Processe um seletor com um único thread. Para usar o seletor, você deve registrar o canal no seletor e depois chamar seu método select (). Este método será bloqueado até que um canal registrado tenha o evento pronto. Depois que esse método retornar, o tópico pode processar esses eventos, como novas conexões, recepção de dados, etc.
Observação:
1. Que modelo selecionado?
Select é um mecanismo de acionamento de eventos, que aciona o processamento quando o evento de espera ocorre e é usado principalmente para o processamento do cliente pela implementação do Linux de servidores.
Ele pode detectar simultaneamente um conjunto de dispositivos de IO não bloqueadores que suportam não bloqueio, se existem eventos (como saída legível, gravável e de alta prioridade, etc.) até que um dispositivo desencadeie um evento ou exceda o tempo de espera especificado. Ou seja, a responsabilidade deles é não fazer IO, mas ajudar o chamador a encontrar o dispositivo atualmente pronto.
2. Qual é o modelo Epoll?
A idéia de design do epoll é dividir a única operação de seleção/pesquisa em 1 epoll_create + múltiplo epoll_ctrl + uma espera. Além disso, o kernel adicionou um sistema de arquivos "EventPollfs" para operações da Epoll. Cada ou mais descritores de arquivo a serem monitorados têm um nó inode do sistema de arquivos EventPollfs correspondente, e as informações principais são armazenadas na estrutura do EventPoll. As informações importantes dos arquivos monitorados são armazenados na estrutura do epitem. Então eles são um relacionamento um para muitos.
2. Desenvolvimento do lado do servidor Nio
Função Descrição: Ligue o lado do servidor e envie a String Hello para cada cliente de acesso.
Existem várias etapas principais para usar o NIO para o desenvolvimento do servidor:
1. Crie o ServerSocketchAnnel e configure-o para o modo não bloqueador
serverSocketchAnnel = serversocketchannel.open (); Serversocketchannel.configureblocking (false);
2. Ligue a escuta e configure os parâmetros TCP, como o tamanho do backlog
Serversocketchannel.socket (). Bind (new inetSocketAddress (8080));
3. Crie um thread de E/S independente para a pesquisa do seletor de multiplexador
4. Crie um seletor, registre o ServerSocketchannel que você criou anteriormente para o seletor e ouça para seleção
seletor = selettor.open (); Serversocketchannel.register (Selector, SelectionKey.op_accept);
5. Inicie o thread de E/S, execute o método selettor.Select () no corpo do loop e pesquise o canal pronto
Enquanto (true) {tente {// select () blocos até que pelo menos um canal esteja pronto no evento que você registrou // se não houver canal pronto, ele bloqueará aqui // Selecionar (tempo limite longo) é o mesmo que selecion (), exceto que ele bloqueará o tempo limite milissegundos (parâmetros). selector.Select (); } catch (ioexception e) {// TODO GATO GENERADO AUTOMENTADO BLOCO E.PRINTSTACKTRACE (); quebrar; }}6. Ao pesquisar o canal no estado pronto, ele precisa ser julgado. Se for o estado do OP_ACECT, significa que é um novo acesso ao cliente. Em seguida, ligue para o Método Serversocketchannel.accept () para aceitar o novo cliente.
// Retorna o Ready SelectionKey e, em seguida, itera para executar o conjunto <SelectionKey> readkeys = Selector.SelectedKeys (); for (iterator <SelectionKey> it = readkeys.iterator (); it.hasnext ();) {seleçãokey key = it.next (); it.remove (); tente {if (key.isAcceptable ()) {serverSocketchAnnel Server = (ServerSocketchannel) key.Channel (); Socketchannel cliente = server.accept (); client.configureblocking (false); client.register (seletor, SelectionKey.op_write); } else if (key.iswritable ()) {socketchannel client = (socketchannel) key.channel (); Buffer bytebuffer = bytebuffer.Allocate (20); String str = "Hello"; buffer = bytebuffer.wrap (str.getBytes ()); client.write (buffer); key.cancel (); }} catch (ioexception e) {e.printStackTrace (); key.cancel (); tente {key.channel (). Close (); } catch (ioexception e1) {// TODO GATO GENERADO AUTOMENTADO BLOCO E1.PRINTSTACKTRACE (); }}}7. Defina o recém-acessado Link Socketchannel no modo não bloqueador e configure alguns outros parâmetros TCP.
if (key.isacceptable ()) {serverSocketchAnnel Server = (ServerSocketchAnnel) key.Channel (); Socketchannel cliente = server.accept (); client.configureblocking (false); ...}8. Registre -se o Socketchannel no seletor e ouça Op_write
client.register (seletor, SelectionKey.op_write);
9. Se o canal entregue for op_write, significa que os dados devem ser gravados no SockChannel, o objeto ByteBuffer será construído e o pacote de dados será gravado.
caso contrário, if (key.iswritable ()) {socketchannel client = (socketchannel) key.channel (); Buffer bytebuffer = bytebuffer.Allocate (20); String str = "Hello"; buffer = bytebuffer.wrap (str.getBytes ()); client.write (buffer); key.cancel (); }O código completo é o seguinte:
importar java.io.ioException; importar java.net.inetsocketaddress; importar java.nio.bytebuffer; importar java.nio.channels.selectionKey; importen.nio.nio.channels.selector; importação java.nio.channels.setoreector; java.nio.channels.socketchannel; importar java.util.iterator; importar java.util.set; public class ServerSocketchAnnelDemo {public static void main (string [] args) {serversocketnannel serversocketchannel; selettor selettor = null; try) {serversockannel Serversocketchannel.open (); serversocketchannel.configureblocking (false); serverSocketchannel.socket (). blocke.printStackTrace ();} while (true) {tente {// select () blocos até que pelo menos um canal esteja pronto no evento que você registrou // se não houver canal pronto, ele bloqueará aqui o tempo todo. selettor.Select ();} Catch (ioException e) {// TODO GENERATO AUTOMENTADO Blocke.printStackTrace (); Break;} // Retorne o Ready SelectionKey e depois itera para (Iterator <sectKeyKey> readKeys = selector.SelectedKeys (); para (ITERTOR <SELECTHEY> {SeleçãokeKey key = it.next (); it.remove (); tente {if (key.isAcceptable ()) {serversocketchannel server = (serversocketchannel) key.channel (); #register.retem. if (key.isWritable ()) {socketchannel client = (socketchannel) key.channel (); buffer bytebuffer = bytebuffer.Allocate (20); string str = "hello"; buffer = byteBuffer.wrap (str.Getes ()); »»Write (tampão); {E.PrintStackTrace (); key.cancel (); tente {key.channel (). Close ();} catch (ioexception e1) {// TODO Blocke1.printstacktrace ();}}}}}}}}}}}}}}}}}}}Usamos o Telnet localhost 8080 para simular vários clientes:
Os resultados do programa são os seguintes:
Resumir
O exposto acima é toda a explicação detalhada do desenvolvimento do lado do servidor Java Nio neste artigo, espero que seja útil para todos. Amigos interessados podem continuar se referindo a outros tópicos relacionados neste site. Se houver alguma falha, deixe uma mensagem para apontá -la. Obrigado amigos pelo seu apoio para este site!