1. บทนำสู่ NIO Library
1. บัฟเฟอร์บัฟเฟอร์
บัฟเฟอร์เป็นวัตถุที่มีข้อมูลบางอย่างที่จะเขียนและอ่านออก
ใน NIO ข้อมูลทั้งหมดจะถูกประมวลผลในบัฟเฟอร์ เมื่ออ่านข้อมูลจะถูกอ่านโดยตรงจากช่องไปยังบัฟเฟอร์และเมื่อเขียนข้อมูลมันจะถูกเขียนจากบัฟเฟอร์ไปยังช่อง
บัฟเฟอร์นั้นเป็นอาร์เรย์ซึ่งมักจะเป็นอาร์เรย์ไบต์ (Bytebuffer) หรืออาร์เรย์ประเภทอื่น ๆ นอกจากนี้บัฟเฟอร์ยังให้ข้อมูลเช่นการเข้าถึงข้อมูลที่มีโครงสร้างและบำรุงรักษาสถานที่อ่านและเขียน
ความสัมพันธ์การสืบทอดของคลาสบัฟเฟอร์แสดงในรูปด้านล่าง:
2. ช่อง
ช่องเป็นช่องที่ข้อมูลเครือข่ายถูกอ่านและเขียนผ่านช่องสัญญาณ ความแตกต่างระหว่างช่องและสตรีมคือช่องเป็นสองทิศทาง (ช่องสามารถใช้ในการอ่านและเขียนหลังในเวลาเดียวกัน) และสตรีมจะเคลื่อนที่ไปในทิศทางเดียวเท่านั้น
ช่องสามารถแบ่งออกเป็นสองประเภท: SelectableChannel สำหรับการอ่านและการเขียนเครือข่าย (ServersocketChannel และ Socketchannel เป็นคลาสย่อยของพวกเขา) และ FileChannel สำหรับการดำเนินการไฟล์
ตัวอย่างต่อไปนี้แสดงไฟล์โดยใช้ FileChannel เพื่อเขียนข้อมูลไปยังไฟล์อ่านข้อมูลจากไฟล์และคัดลอกข้อมูลไฟล์ไปยังไฟล์อื่น:
ระดับสาธารณะ niotest {โมฆะสาธารณะคงที่หลัก (สตริง [] args) พ่น ioexception {copyfile (); } // คัดลอกไฟล์โมฆะส่วนตัวแบบคงที่ copyfile () {fileInputStream ใน = null; fileOutputStream out = null; ลอง {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 (บัฟเฟอร์); ในขณะที่ (bytesRead! =-1) {buffer.flip (); outchannel.write (บัฟเฟอร์); buffer.clear (); bytesRead = inchannel.read (บัฟเฟอร์); }} catch (filenotfoundexception e) {// todo บล็อก catch block ที่สร้างขึ้นอัตโนมัติ e.printstacktrace (); } catch (ioexception e) {// todo บล็อก catch block ที่สร้างอัตโนมัติ e.printstacktrace (); }} // เขียนไฟล์ส่วนตัวคงที่ static writefilenio () {ลอง {randomaccessFile fout = new RandomActEncessFile ("src/main/java/data/nio-data.txt", "rw"); FileChannel fc = fout.getChannel (); bytebuffer buffer = bytebuffer.allocate (1024); buffer.put ("hi123" .getBytes ()); buffer.flip (); ลอง {fc.write (บัฟเฟอร์); } catch (ioexception e) {// todo บล็อก catch block ที่สร้างอัตโนมัติ e.printstacktrace (); }} catch (filenotfoundexception e) {// todo บล็อก catch block ที่สร้างขึ้นอัตโนมัติ e.printstacktrace (); }} // อ่านไฟล์ส่วนตัวคงที่ readfilenio () {fileInputStream fileInputStream; ลอง {fileInputStream = ใหม่ FileInputStream ("SRC/Main/Java/Data/Nio-Data.txt"); fileChannel fileChannel = fileInputStream.getChannel (); // รับ channel จาก fileInputStream bytebuffer bytebuffer = bytebuffer.allocate (1024); // สร้างบัฟเฟอร์ int bytesread = fileChannel.Read (byteBuffer); */ bytebuffer.flip (); // hasremaining (): แจ้งว่ามีองค์ประกอบระหว่างตำแหน่งปัจจุบันและขีด จำกัด ในขณะที่ (bytebuffer.hasremaining ()) {system.out.print ((char) bytebuffer.get ()); } /** ล้างบัฟเฟอร์* ตำแหน่ง = 0; * ขีด จำกัด = ความจุ; */ bytebuffer.clear (); bytesRead = fileChannel.read (bytebuffer); }} catch (filenotfoundexception e) {// todo บล็อก catch block ที่สร้างขึ้นอัตโนมัติ e.printstacktrace (); } catch (ioexception e) {// todo บล็อก catch block ที่สร้างอัตโนมัติ e.printstacktrace (); -3. ตัวเลือกมัลติเพล็กเซอร์
มัลติเพล็กเซอร์ให้ความสามารถในการเลือกงานที่พร้อม ตัวเลือกจะสำรวจช่องทางที่ลงทะเบียนอย่างต่อเนื่อง หากช่องส่งเหตุการณ์การอ่านหรือเขียนช่องจะอยู่ในสถานะพร้อมและจะถูกสำรวจโดยตัวเลือก จากนั้นชุดของช่องพร้อมรับสามารถรับได้ผ่านการเลือกคีย์เพื่อดำเนินการ I/O ที่ตามมา
ตัวเลือกมัลติเพล็กเซอร์สามารถสำรวจหลายช่องทางในเวลาเดียวกัน เนื่องจาก JDK ใช้ EPOLL แทนการใช้งานแบบเลือกแบบดั้งเดิมจึงไม่มีขีด จำกัด ของการเชื่อมต่อการเชื่อมต่อสูงสุด 1024/2048 ซึ่งหมายความว่ามีเพียงเธรดเดียวเท่านั้นที่ต้องรับผิดชอบในการสำรวจความคิดเห็นของตัวเลือกและสามารถเข้าถึงลูกค้าหลายพันราย โมเดลแสดงในรูปด้านล่าง:
ประมวลผลตัวเลือกด้วยเธรดเดียว ในการใช้ตัวเลือกคุณต้องลงทะเบียนช่องด้วยตัวเลือกจากนั้นเรียกใช้วิธีการเลือก () วิธีนี้จะบล็อกจนกว่าช่องทางที่ลงทะเบียนจะพร้อมเหตุการณ์ เมื่อวิธีนี้ส่งคืนเธรดสามารถประมวลผลเหตุการณ์เหล่านี้เช่นการเชื่อมต่อใหม่การรับข้อมูล ฯลฯ
บันทึก:
1. รุ่นเลือกอะไร?
Select เป็นกลไกการกระตุ้นเหตุการณ์ซึ่งกระตุ้นการประมวลผลเมื่อเหตุการณ์รอเกิดขึ้นและส่วนใหญ่จะใช้สำหรับการประมวลผลของลูกค้าโดยการใช้งาน Linux ของเซิร์ฟเวอร์
มันสามารถตรวจจับชุดอุปกรณ์ IO ที่ไม่ปิดกั้นที่รองรับการไม่ปิดกั้นไม่ว่าจะมีเหตุการณ์ (เช่นการอ่านได้ง่ายและมีลำดับความสำคัญที่มีลำดับความสำคัญ ฯลฯ ) จนกว่าอุปกรณ์จะกระตุ้นเหตุการณ์หรือเกินเวลารอคอยที่ระบุ นั่นคือความรับผิดชอบของพวกเขาคือไม่ต้องทำ IO แต่เพื่อช่วยให้ผู้โทรค้นหาอุปกรณ์ที่พร้อมในปัจจุบัน
2. รุ่น epoll คืออะไร?
แนวคิดการออกแบบของ epoll คือการแยกการดำเนินการเดี่ยวของการเลือก/โพลออกเป็น 1 epoll_create + หลาย epoll_ctrl + หนึ่งรอ นอกจากนี้เคอร์เนลได้เพิ่มระบบไฟล์ "EventPollfs" สำหรับการดำเนินการ EPOLL ตัวอธิบายไฟล์แต่ละตัวหรือมากกว่าที่จะตรวจสอบมีโหนด inode ของระบบไฟล์ EventPollfs ที่สอดคล้องกันและข้อมูลหลักจะถูกเก็บไว้ในโครงสร้าง EventPoll ข้อมูลสำคัญของไฟล์ที่ตรวจสอบจะถูกเก็บไว้ในโครงสร้าง epitem ดังนั้นพวกเขาจึงเป็นความสัมพันธ์แบบหนึ่งต่อหลายคน
2. การพัฒนาฝั่งเซิร์ฟเวอร์ NIO
ฟังก์ชั่นคำอธิบาย: เปิดด้านเซิร์ฟเวอร์และส่งสตริงสวัสดีไปยังไคลเอนต์การเข้าถึงแต่ละรายการ
มีหลายขั้นตอนหลักสำหรับการใช้ NIO สำหรับการพัฒนาฝั่งเซิร์ฟเวอร์:
1. สร้าง serversocketChannel และกำหนดค่าเป็นโหมดที่ไม่ปิดกั้น
serversocketChannel = serversocketChannel.open (); ServersocketChannel.ConfigureBlocking (เท็จ);
2. ผูกพารามิเตอร์การฟังและกำหนดค่าพารามิเตอร์ TCP เช่นขนาด backlog
ServersocketChannel.socket (). ผูก (ใหม่ inetSocketAddress (8080));
3. สร้างเธรด I/O อิสระสำหรับการสำรวจตัวเลือกมัลติเพล็กเซอร์
4. สร้างตัวเลือกลงทะเบียน serversocketChannel ที่คุณสร้างไว้ก่อนหน้านี้กับตัวเลือกและฟัง selectionKey.ccept
ตัวเลือก = selector.open (); ServersocketChannel.register (ตัวเลือก, selectionKey.op_accept);
5. เริ่มต้นเธรด I/O, ดำเนินการวิธีการเลือก SELECTOR.SELECT () ในตัววนลูปและสำรวจช่องทางพร้อม
ในขณะที่ (จริง) {ลอง {// select () บล็อกจนกระทั่งอย่างน้อยหนึ่งช่องพร้อมกับเหตุการณ์ที่คุณลงทะเบียน // หากไม่มีช่องพร้อมมันจะบล็อกที่นี่ // select (การหมดเวลายาว) เหมือนกับเลือก () ยกเว้นว่ามันจะบล็อกมิลลิวินาทีหมดเวลา (พารามิเตอร์) selector.select (); } catch (ioexception e) {// todo บล็อก catch block ที่สร้างอัตโนมัติ e.printstacktrace (); หยุดพัก; -6. เมื่อสำรวจช่องทางในสถานะพร้อมจะต้องได้รับการตัดสิน หากเป็นสถานะ op_accept หมายความว่าเป็นการเข้าถึงไคลเอนต์ใหม่ จากนั้นเรียกใช้เมธอด serversocketChannel.accept () เพื่อรับไคลเอนต์ใหม่
// ส่งคืน ready selectionKey จากนั้นวนซ้ำเพื่อเรียกใช้ Set <SelectionKey> readKeys = selector.selectedKeys (); สำหรับ (iterator <SelectionKey> it = readKeys.iterator (); it.hasnext ();) {selectionKey key = it.next (); it.remove (); ลอง {if (key.iscceptable ()) {serversocketChannel server = (serversocketChannel) key.channel (); SocketChannel Client = Server.accept (); client.configureblocking (เท็จ); client.register (ตัวเลือก, selectionKey.op_write); } อื่นถ้า (key.iswitable ()) {socketchannel client = (socketchannel) key.channel (); bytebuffer buffer = bytebuffer.allocate (20); string str = "สวัสดี"; buffer = byteBuffer.wrap (str.getBytes ()); client.write (บัฟเฟอร์); key.cancel (); }} catch (ioexception e) {e.printstacktrace (); key.cancel (); ลอง {key.channel (). close (); } catch (ioexception e1) {// todo catch block catch ที่สร้างอัตโนมัติ e1.printstacktrace (); -7. ตั้งค่า SocketChannel ลิงค์ไคลเอ็นต์ที่เข้าถึงใหม่เป็นโหมดที่ไม่ปิดกั้นและกำหนดค่าพารามิเตอร์ TCP อื่น ๆ
if (key.isacceptable ()) {serversocketChannel Server = (serversocketChannel) key.channel (); SocketChannel Client = Server.accept (); client.configureblocking (เท็จ); -8. ลงทะเบียน socketchannel เพื่อเลือกและฟัง op_write
client.register (ตัวเลือก, selectionKey.op_write);
9. หากช่องแบบสำรวจคือ op_write หมายความว่าจะต้องเขียนข้อมูลลงใน Sockchannel แล้ววัตถุ Bytebuffer จะถูกสร้างขึ้นและเขียนแพ็คเก็ตข้อมูล
อื่นถ้า (key.iswitable ()) {socketchannel client = (socketchannel) key.channel (); bytebuffer buffer = bytebuffer.allocate (20); string str = "สวัสดี"; buffer = byteBuffer.wrap (str.getBytes ()); client.write (บัฟเฟอร์); key.cancel (); -รหัสที่สมบูรณ์มีดังนี้:
นำเข้า java.io.ioexception; นำเข้า java.net.inetsocketaddress; นำเข้า java.nio.bytebuffer; นำเข้า java.nio.channels.SelectionKey; นำเข้า java.nio.channels.Selector; java.nio.channels.socketChannel; นำเข้า java.util.iterator; นำเข้า java.util.set; คลาสสาธารณะ serversocketChanneldemo {โมฆะคงที่สาธารณะหลัก (สตริง [] args) ServersocketChannel.open (); ServersocketChannel.ConfigureBlocking (เท็จ); ServersocketChannel.Socket (). ผูก (ใหม่ inetSocketAddress (8080)); selector = selector.Open (); severtocketCannel.rective blocke.printstacktrace ();} ในขณะที่ (จริง) {ลอง {// select () บล็อกจนกระทั่งอย่างน้อยหนึ่งช่องพร้อมกับเหตุการณ์ที่คุณลงทะเบียน // หากไม่มีช่องทางพร้อมมันจะบล็อกที่นี่ตลอดเวลา selector.select ();} catch (ioexception e) {// todo catch blocke.printstacktrace (); break;} // ส่งคืน ready selectionKey แล้ววนซ้ำ {selectionKey key = it.next (); it.remove (); ลอง {ถ้า (key.iscceptable ()) {serversocketChannel server = (serversocketChannel) key.channel (); socketchannel client = server.ccept (); if (key.iswitable ()) {socketchannel client = (socketchannel) key.channel (); bytebuffer buffer = bytebuffer.allocate (20); str = "hello"; buffer = bytebuffer.wrap (str.getBytes (); client.write {E.printStackTrace (); key.cancel (); ลอง {key.channel (). close ();} catch (ioexception e1) {// toDo catch blocke1.printstacktrace ();}}}}}}}}}}}}}}}}}}}}}}}}}}}เราใช้ Telnet LocalHost 8080 เพื่อจำลองลูกค้าหลายราย:
ผลลัพธ์การรันโปรแกรมมีดังนี้:
สรุป
ข้างต้นเป็นคำอธิบายโดยละเอียดทั้งหมดของการพัฒนาฝั่งเซิร์ฟเวอร์ Java Nio ในบทความนี้ฉันหวังว่ามันจะเป็นประโยชน์กับทุกคน เพื่อนที่สนใจสามารถอ้างถึงหัวข้ออื่น ๆ ที่เกี่ยวข้องในเว็บไซต์นี้ต่อไป หากมีข้อบกพร่องใด ๆ โปรดฝากข้อความไว้เพื่อชี้ให้เห็น ขอบคุณเพื่อนที่ให้การสนับสนุนเว็บไซต์นี้!