Sistem berikut menyediakan pengantar mendalam untuk mekanisme fungsi dan penggunaan fungsi Java Nio melalui prinsip-prinsip, proses, dll., Mari kita pelajari.
Kata pengantar
Artikel ini terutama menjelaskan mekanisme IO di Java
Dibagi menjadi dua bagian:
Bagian pertama menjelaskan mekanisme IO di bawah multithreading. Bagian kedua menjelaskan cara mengoptimalkan pemborosan sumber daya CPU di bawah mekanisme IO (IO baru)
Echo Server
Saya tidak perlu memperkenalkan mekanisme soket di bawah satu utas. Jika Anda tidak tahu, Anda dapat memeriksa informasi di bawah begitu banyak utas. Bagaimana jika Anda menggunakan soket?
Kami menggunakan server gema paling sederhana untuk membantu semua orang memahami
Pertama, mari kita lihat diagram alur kerja server dan klien di bawah multi-threading:
Anda dapat melihat bahwa banyak klien mengirim permintaan ke server secara bersamaan
Server telah membuat ukuran untuk memungkinkan beberapa utas untuk mencocokkan klien yang sesuai.
Dan setiap utas melengkapi permintaan klien mereka sendiri
Setelah prinsip selesai, mari kita lihat bagaimana itu diterapkan
Di sini saya menulis server sederhana
Gunakan Teknologi Utas Pool untuk membuat utas (saya telah mengomentari fungsi kode tertentu):
kelas publik myserver {private static executorservice executorservice = executors.newcachedthreadpool (); // Buat Thread Pool Private Static Class HandLemsG mengimplementasikan Runnable {// Setelah ada permintaan klien baru, buat utas ini untuk memproses klien soket; // Buat handlemsg publik klien (klien soket) {// buat parameter yang mengikat this.client = klien; } @Override public void run () {bufferedReader bufferedReader = null; // Buat cache character input stream printwriter printwriter = null; // Buat stream penulisan karakter coba {bufferedReader = new BufferedReader (inputStreamReader baru (client.getInputStream ())); // Dapatkan printwriter aliran input klien = printwriter baru (client.getoutputStream (), true); // Dapatkan aliran output klien, benar adalah menyegarkan string inputline = null; long a = system.currentTimemillis (); while ((inputline = bufferedReader.readline ())! = null) {printwriter.println (inputline); } long b = system.currentTimeMillis (); System.out.println ("Utas ini mengambil:"+(BA)+"detik!"); } catch (ioException e) {e.printstacktrace (); } akhirnya {coba {bufferedReader.close (); printwriter.close (); client.close (); } catch (ioException e) {e.printstacktrace (); }}}} public static void main (string [] args) melempar ioException {// utas utama server digunakan untuk mendengarkan permintaan klien dalam server server loop = server baru (8686); // Buat server dengan Port 8686 Socket Client = NULL; while (true) {// loop listen client = server.accept (); // Server mendengarkan sistem permintaan klien.out.println (client.getRemoteSocketAddress ()+"Koneksi klien berhasil!"); ExecutorService.submit (handlemsg baru (klien)); // Masukkan permintaan klien ke utas HandlMSG melalui kumpulan utas untuk diproses}}}Dalam kode di atas, kami menggunakan kelas untuk menulis server Echo sederhana untuk mengaktifkan mendengarkan port di utas utama menggunakan loop dead.
Klien Sederhana
Dengan server, kami dapat mengaksesnya, dan mengirim beberapa data string. Fungsi server adalah untuk mengembalikan string ini dan mencetak waktu yang membutuhkan waktu.
Mari kita tulis klien sederhana untuk menanggapi server:
kelas publik myClient {public static void main (string [] args) melempar ioException {socket client = null; Printwriter printwriter = null; BufferedReader bufferedReader = null; coba {klien = soket baru (); client.connect (inetsocketAddress baru ("localhost", 8686)); printwriter = printwriter baru (client.getoutputStream (), true); printwriter.println ("halo"); printwriter.flush (); bufferedReader = BufferedReader baru (inputStreamReader baru (client.getInputStream ())); // Baca informasi yang dikembalikan oleh server dan output system.out.println ("Informasi dari server adalah:"+bufferedReader.readline ()); } catch (ioException e) {e.printstacktrace (); } akhirnya {printwriter.close (); bufferedreader.close (); client.close (); }}}Dalam kode, kami menggunakan aliran karakter untuk mengirim hello string. Jika kode baik -baik saja, server akan mengembalikan data Hello dan mencetak informasi log yang kami atur.
Tampilan hasil server gema
Ayo jalankan:
1. Buka server dan aktifkan Loop Listening:
2. Buka klien:
Anda dapat melihat bahwa klien mencetak hasil pengembalian
3. Periksa log server:
Sangat bagus, pemrograman soket multithreaded sederhana diimplementasikan
Tapi pikirkanlah:
Jika klien meminta, tambahkan tidur selama menulis IO ke server,
Buat setiap permintaan menempati utas server selama 10 detik
Lalu ada banyak permintaan klien, masing -masing memakan waktu selama itu
Maka kemampuan unifikasi server akan sangat berkurang
Ini bukan karena server memiliki banyak tugas berat, tetapi hanya karena utas layanan sedang menunggu IO (karena terima, baca, dan tulis semuanya memblokir)
Sangat tidak dibayar untuk membiarkan CPU berkecepatan tinggi menunggu jaringan mereka yang tidak efisien
Apa yang harus saya lakukan saat ini?
Nio
IO baru berhasil memecahkan masalah di atas. Bagaimana cara menyelesaikannya?
Unit minimum untuk IO untuk memproses permintaan klien adalah utas
NIO menggunakan unit yang satu level lebih kecil dari utas: saluran (saluran)
Dapat dikatakan bahwa hanya satu utas yang diperlukan di NIO untuk menyelesaikan semua penerimaan, membaca, menulis, dan operasi lainnya.
Untuk mempelajari NIO, Anda harus terlebih dahulu memahami tiga poin intinya
Pemilih, pemilih
Buffer, buffer
Saluran, saluran
Blogger itu tidak berbakat, jadi dia menggambar gambaran yang buruk untuk memperdalam kesannya^. ^
Beri saya diagram alur kerja NIO lainnya di bawah TCP (sangat sulit untuk menggambar garis ...)
Memahaminya secara kasar, ayo langkah demi langkah
Penyangga
Pertama -tama, Anda perlu tahu apa itu penyangga
Interaksi data tidak lagi menggunakan aliran seperti mekanisme IO
Sebagai gantinya, gunakan buffer (buffer)
Blogger berpikir bahwa gambar adalah yang termudah untuk dipahami
Jadi...
Anda dapat melihat di mana buffer berada di seluruh alur kerja
Mari kita lihat yang sebenarnya. Kode spesifik pada gambar di atas adalah sebagai berikut:
1. Pertama mengalokasikan ruang untuk buffer, dalam byte
Bytebuffer bytebuffer = bytebuffer.allocate (1024);
Buat Objek ByteBuffer dan tentukan ukuran memori
2. Tulis data ke buffer:
1). Data dari saluran ke buffer: channel.read (bytebuffer); 2). Data dari klien ke buffer: bytebuffer.put (...);
3. Baca data dari buffer:
1). Data dari buffer ke saluran: channel.write (bytebuffer); 2). Data dari buffer ke server: bytebuffer.get (...);
Pemilih
Pemilih adalah inti dari NIO, itu adalah administrator saluran
Dengan mengeksekusi metode pemblokiran pilih (), dengarkan apakah saluran sudah siap
Setelah data dapat dibaca, nilai pengembalian metode ini adalah jumlah selectionkeys
Jadi server biasanya mengeksekusi metode terpilih () loop dead sampai saluran siap dan kemudian mulai berfungsi
Setiap saluran akan mengikat acara ke pemilih, dan kemudian menghasilkan objek seleksi.
Apa yang harus dicatat adalah:
Ketika saluran dan pemilih terikat, saluran harus dalam mode non-blocking.
FileChannel tidak dapat beralih ke mode non-blocking karena bukan saluran soket, jadi filechannel tidak dapat mengikat peristiwa dengan pemilih
Ada empat jenis acara di Nio:
1.SelectionKey.OP_Connect: Acara Koneksi
2.SelectionKey.op_accept: Terima acara
3.SelectionKey.OP_READ: Baca acara
4.SelectionKey.OP_WRITE: Tulis acara
Saluran
Total ada empat saluran:
FileChannel: Bertindak pada aliran file IO
Datagramchannel: Ini berfungsi pada protokol UDP
Socketchannel: Ini bekerja pada protokol TCP
ServerSocketchannel: itu bertindak pada protokol TCP
Artikel ini menjelaskan NIO melalui protokol TCP yang umum digunakan
Mari kita ambil serverchetchannel sebagai contoh:
Buka Saluran Serverchetchannel
ServerSocketchannel ServerSocketchannel = serversocketchannel.open ();
Tutup Saluran Serversocketchannel:
serversocketchannel.close ();
Looping Socketchannel:
while (true) {socketchannel socketchannel = serversocketchannel.accept (); clientchannel.configureblocking (false);} clientChannel.configureBlocking(false); Pernyataan adalah untuk mengatur saluran ini ke non-blocking, yaitu, kontrol bebas yang tidak sinkron dari pemblokiran atau non-blocking adalah salah satu karakteristik NIO
Seleksi
SelectionKey adalah komponen inti dari interaksi antara saluran dan pemilih
Misalnya, ikat pemilih pada socketchannel dan daftarkan sebagai acara koneksi:
Socketchannel clientchannel = socketchannel.open (); clientchannel.configureblocking (false); clientchannel.connect (inetsocketaddress baru (port)); clientchannel.register (selector, selectionkey.op_connect);
Inti adalah dalam metode register (), yang mengembalikan objek seleksi
Untuk mendeteksi acara saluran adalah acara seperti apa yang dapat Anda gunakan metode berikut:
selectionKey.isacceptable (); selectionKey.isconnectable (); selectionKey.isreadable (); selectionKey.iswritable ();
Server melakukan operasi yang sesuai dalam pemungutan suara melalui metode ini
Tentu saja, kunci yang terikat ke saluran dan pemilih juga dapat diperoleh secara bergantian.
Saluran saluran = selectionKey.channel (); selector selector = selectionKey.selector ();
Saat mendaftarkan acara di saluran, kami juga dapat mengikat buffer:
clientchannel.register (key.selector (), selectionkey.op_read, bytebuffer.allocatedirect (1024));
Atau mengikat objek:
selectionKey.Attach (objek); objek anthorobj = selectionKey.attachment ();
Server TCP Nio
Setelah banyak berbicara, kita akan melihat kode inti yang paling sederhana dan paling inti (tidak elegan untuk menambahkan begitu banyak komentar, tetapi lebih mudah bagi semua orang untuk memahami):
Paket cn.blog.test.niotest; import java.io.ioexception; impor java.net.inetsocketaddress; impor java.nio.bytebuffer; import java.nio.channels.*; impor java.nio.charset.charset; impor java.util.utiRs. pemilih; // Buat port int statis final swasta pemilih = 8686; private final static int buf_size = 10240; private void initServer () melempar ioException {// buat saluran manajer saluran selector this.selector = selector.open (); // Buat server channel channel channelSocketchannel = serversocketchannel.open (); channel.configureblocking (false); // atur saluran ke saluran non-blocking.socket (). Bind (inetsocketAddress baru (port)); // Bind saluran pada port 8686 // ikat manajer dan saluran saluran di atas, dan daftarkan acara op_accept untuk saluran // setelah mendaftarkan acara, selector.select () akan mengembalikan (kunci). Jika acara tidak mencapai selector.select (), ia akan memblokir seleksi seleksiKey = channel.register (selector, selectionkey.op_accept); while (true) {// polling selector.select (); // Ini adalah metode pemblokiran, tunggu sampai data dapat dibaca, dan nilai pengembalian adalah jumlah kunci (bisa ada beberapa) set tombol set = selector.selectedKeys (); // Jika saluran memiliki data, akses kunci yang dihasilkan ke dalam koleksi tombol iterator = keys.iterator (); // Dapatkan iterator koleksi tombol ini sementara (iterator.hasnext ()) {// Gunakan iterator untuk melintasi koleksi collection key = (selection) iterator.next (); // Dapatkan instance kunci dalam koleksi iterator.remove (); // Ingatlah untuk menghapus elemen ini di iterator setelah mendapatkan instance kunci saat ini, yang sangat penting, jika tidak kesalahan akan terjadi jika (key.isacceptable ()) {// menilai apakah saluran yang diwakili oleh kunci saat ini dalam keadaan yang dapat diterima, dan jika demikian, doaccept (kunci); } else if (key.isreadable ()) {doread (key); } else if (key.iswritable () && key.isValid ()) {dowrite (key); } else if (key.isconnectable ()) {System.out.println ("Connectable!"); }}}} public void doaccept (kunci selection) melempar ioException {serversocketchannel serverchannel = (serversocketchannel) key.channel (); System.out.println ("ServerSocketchannel mendengarkan dalam lingkaran"); Socketchannel clientchannel = serverchannel.accept (); clientchannel.configureblocking (false); clientchannel.register (key.selector (), selectionkey.op_read); } public void doread (kunci seleksi) melempar ioException {socketchannel clientchannel = (socketchannel) key.channel (); Bytebuffer byteBuffer = byteBuffer.allocate (buf_size); long bytesread = clientchannel.read (bytebuffer); while (bytesread> 0) {byteBuffer.flip (); byte [] data = byteBuffer.Array (); Info string = string baru (data) .trim (); System.out.println ("Pesan yang dikirim dari klien adalah:"+info); bytebuffer.clear (); bytesread = clientchannel.read (bytebuffer); } if (bytesread ==-1) {clientchannel.close (); }} public void dowrite (kunci selection) melempar ioException {byteBuffer byteBuffer = byteBuffer.allocate (buf_size); bytebuffer.flip (); Socketchannel clientchannel = (socketchannel) key.channel (); while (byteBuffer.hasremaining ()) {clientchannel.write (byteBuffer); } byteBuffer.compact (); } public static void main (string [] args) melempar ioException {mynioserver mynioserver = mynioserver baru (); mynioserver.initserver (); }} Saya mencetak saluran monitor dan memberi tahu Anda ketika server yang mulai berjalan
Jika Anda bekerja sama dengan debug klien NIO, Anda dapat menemukannya dengan jelas. Sebelum memasukkan jajak pendapat Select ()
Meskipun sudah ada kunci untuk acara Accept, Select () tidak akan dipanggil secara default
Sebaliknya, Anda harus menunggu sampai acara menarik lainnya ditangkap oleh Select () sebelum menelepon SectionKey seleksi ACCEPT
Baru dengan demikian server yang terbiasa mulai melakukan pemantauan melingkar
Dengan kata lain, dalam pemilih, ServerSocketchannel selalu dipertahankan.
Dan serverChannel.accept(); benar -benar asinkron (channel.configureblocking (false); dalam metode initserver)
Jika koneksi tidak diterima, nol akan dikembalikan
Jika socketchannel berhasil terhubung, Socketchannel ini mendaftarkan acara tulis (baca)
Dan atur asinkron
Klien TCP Nio
Harus ada klien jika ada server
Faktanya, jika Anda dapat sepenuhnya memahami server
Kode klien serupa
Paket cn.blog.test.niotest; impor java.io.ioexception; impor java.net.inetsocketaddress; impor java.nio.bytebuffer; impor java.nio.channels.selectioney; import java.nio.channels.selector; impor java.nio.nio.nio.nio.noVels. Mynioclient {pemilih pemilih swasta; // Buat port int statis final swasta pemilih = 8686; private final static int buf_size = 10240; private static bytebuffer byteBuffer = byteBuffer.allocate (buf_size); private void initclient () melempar ioException {this.selector = selector.open (); Socketchannel clientchannel = socketchannel.open (); clientchannel.configureblocking (false); clientchannel.connect (inetsocketAddress baru (port)); clientchannel.register (pemilih, selectionkey.op_connect); while (true) {selector.select (); Iterator <dectionKey> iterator = selector.selectedKeys (). Iterator (); while (iterator.hasnext ()) {selectionKey key = iterator.next (); iterator.remove (); if (key.isconnectable ()) {doconnect (key); } else if (key.isreadable ()) {doread (key); }}} public void doconnect (kunci selection) melempar ioException {socketchannel clientchannel = (socketchannel) key.channel (); if (clientchannel.isconnectionpending ()) {clientchannel.finishConnect (); } clientchannel.configureblocking (false); Info String = "Hello Server !!!"; bytebuffer.clear (); byteBuffer.put (info.getbytes ("UTF-8")); bytebuffer.flip (); clientchannel.write (bytebuffer); //clientchannel.register(key.selector(),selectionKey.op_read); clientchannel.close (); } public void doread (kunci seleksi) melempar ioException {socketchannel clientchannel = (socketchannel) key.channel (); clientchannel.read (bytebuffer); byte [] data = byteBuffer.Array (); String msg = string baru (data) .trim (); System.out.println ("Server mengirim pesan:"+msg); clientchannel.close (); key.selector (). Tutup (); } public static void main (string [] args) melempar ioException {mynioclient mynioclient = new mynioclient (); mynioClient.InitClient (); }}Hasil output
Di sini saya membuka server dan dua klien:
Selanjutnya, Anda dapat mencoba membuka seribu klien secara bersamaan. Selama CPU Anda cukup kuat, server tidak akan dapat mengurangi kinerja karena penyumbatan.
Di atas adalah penjelasan terperinci dari Java Nio. Jika Anda memiliki pertanyaan, Anda dapat mendiskusikannya di area pesan di bawah ini.