1. مقدمة لمكتبة NIO
1. العازلة العازلة
المخزن المؤقت هو كائن يحتوي على بعض البيانات التي يجب كتابتها وقراءتها.
في NIO ، تتم معالجة جميع البيانات في المخزن المؤقت. عند قراءة البيانات ، تتم قراءتها مباشرة من القناة إلى المخزن المؤقت ، وعند كتابة البيانات ، يتم كتابتها أيضًا من المخزن المؤقت إلى القناة.
المخزن المؤقت هو في الأساس صفيف ، وعادة ما يكون صفيف بايت (bytebuffer) ، أو أنواع أخرى من المصفوفات. بالإضافة إلى ذلك ، يوفر المخزن المؤقت أيضًا معلومات مثل الوصول المنظم إلى البيانات والحفاظ على مواقع القراءة والكتابة.
وترد العلاقة الميراث للفئة العازلة في الشكل أدناه:
2. القناة
القناة هي قناة تتم فيها قراءة بيانات الشبكة وكتابتها من خلال القناة. الفرق بين القناة والتيار هو أن القناة ثنائية الاتجاه (يمكن استخدام القناة لقراءة وكتابة الأخير في نفس الوقت) ، ويتحرك الدفق فقط في اتجاه واحد.
يمكن تقسيم القنوات تقريبًا إلى فئتين: SelectableChannel لقراءة الشبكة والكتابة (خوادم CosterSocketchannel و Socketchannel هي فئات فرعية) ، و filechannel لعمليات الملفات.
يوضح المثال التالي الملف باستخدام Filechannel لكتابة البيانات إلى ملف ، وقراءة البيانات من ملف ، ونسخ بيانات الملف إلى ملف آخر:
الطبقة العامة niotest {public static void main (string [] args) يلقي ioException {copyFile () ؛ } // انسخ الملف private static void copyfile () {fileInputStream in = null ؛ fileOutputStream Out = null ؛ حاول {في = جديد fileInputStream ("SRC/MAIN/JAVA/DATA/in-data.txt") ؛ out = 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 (buffer) ؛ بينما (bytesread! =-1) {buffer.flip () ؛ Outchannel.write (العازلة) ؛ buffer.clear () ؛ bytesread = inchannel.read (Buffer) ؛ }} catch (fileNotfoundException e) {// todo catch catch e.printstacktrace () ؛ } catch (ioException e) {// todo acto catch block e.printstacktrace () ؛ }} // اكتب ملف خاص باطال ثابت static writefilenio () {try {randomaccessfile fout = new randomaccessfile ("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 (buffer) ؛ } catch (ioException e) {// todo acto catch block e.printstacktrace () ؛ }} catch (fileNotfoundException e) {// todo catch catch e.printstacktrace () ؛ }} // قراءة الملف الخاص readfilenio () {fileInputStream fileInputStream ؛ حاول {fileInputStream = جديد fileInputStream ("src/main/java/data/nio-data.txt") ؛ FILECHANNEL FILECHANNEL = fileInputStream.getChannel () ؛ // احصل على قناة من FileInputStream Bytebuffer Bytebuffer = byteBuffer.Allocate (1024) ؛ // إنشاء مخزن مؤقت int bytesRead = 0 ؛ */ bytebuffer.flip () ؛ // hasremaining (): أبلغ ما إذا كان هناك عنصر بين الموضع الحالي والحد بينما (bytebuffer.hasRemaining ()) {system.out.print ((char) bytebuffer.get ()) ؛ } /** امسح الموضع المخزن المؤقت* = 0 ؛ * الحد = السعة ؛ */ bytebuffer.clear () ؛ bytesread = filechannel.read (bytebuffer) ؛ }} catch (fileNotfoundException e) {// todo catch catch e.printstacktrace () ؛ } catch (ioException e) {// todo acto catch block e.printstacktrace () ؛ }}}3
يوفر مضاعفة الإرسال القدرة على اختيار المهام جاهزة. سيقوم المحدد باستمرار بالاستطلاع القناة المسجلة عليها. إذا أرسلت قناة حدث قراءة أو كتابة ، فستكون القناة في الحالة الجاهزة وستتم استطلاعها من قبل المحدد. بعد ذلك ، يمكن الحصول على مجموعة القنوات الجاهزة من خلال مفتاح التحديد لأداء عمليات الإدخال/الإخراج اللاحقة.
يمكن لمحدد الإرسال المضاعف استطلاع قنوات متعددة في نفس الوقت. نظرًا لأن JDK يستخدم EPOLL بدلاً من تطبيق SELECT التقليدي ، فإنه لا يحتوي على الحد الأقصى لمواجهة الاتصال القصوى 1024/2048 ، مما يعني أنه من الضروري أن يكون موضوع واحد فقط مسؤولاً عن استطلاع المحدد ويمكنه الوصول إلى الآلاف من العملاء. يظهر النموذج في الشكل أدناه:
معالجة محدد مع موضوع واحد. لاستخدام المحدد ، يجب عليك تسجيل القناة مع Selector ثم استدعاء طريقة SELECT (). سيتم حظر هذه الطريقة حتى تصبح القناة المسجلة جاهزة للحدث. بمجرد إرجاع هذه الطريقة ، يمكن للمعالجة معالجة هذه الأحداث ، مثل الاتصالات الجديدة ، واستقبال البيانات ، إلخ.
ملحوظة:
1. ما هو نموذج مختار؟
SELECT هي آلية تثير الحدث ، والتي تؤدي إلى المعالجة عند حدوث حدث الانتظار ، ويستخدم في الغالب لمعالجة العميل من خلال تنفيذ Linux للخوادم.
يمكن أن يكتشف في وقت واحد مجموعة من أجهزة IO غير المحظورة التي تدعم عدم الحظر ، سواء كانت هناك أحداث (مثل إخراج الخطأ القابل للقراءة ، القابلة للكتابة ، ذات الأولوية العالية ، وما إلى ذلك) حتى يؤدي الجهاز إلى حدوث حدث أو يتجاوز وقت الانتظار المحدد. وهذا يعني أن مسؤوليتهم ليست القيام بذلك ، ولكن لمساعدة المتصل في العثور على الجهاز الجاهز حاليًا.
2. ما هو نموذج EPOLL؟
تتمثل فكرة تصميم EPOLL في تقسيم التشغيل الفردي لـ SELECT/POLL إلى 1 EPOLL_CREATE + EPOLL_CTRL + ONE WANT. بالإضافة إلى ذلك ، أضافت kernel نظام ملفات "EventPollfs" لعمليات EPOLL. تحتوي كل أو أكثر من واصفات الملفات المراد مراقبتها على عقدة inode لنظام ملفات EventPollfs المقابل ، ويتم تخزين المعلومات الرئيسية في بنية EventPoll. يتم تخزين المعلومات المهمة للملفات المراقبة في بنية Epitem. لذلك فهي علاقة واحدة إلى حد.
2. تطوير جانب خادم NIO
وصف الوظيفة: قم بتشغيل جانب الخادم وأرسل سلسلة Hello إلى كل عميل وصول.
هناك عدة خطوات رئيسية لاستخدام NIO لتطوير جانب الخادم:
1. إنشاء خادمات soxetChannel وتكوينه إلى وضع عدم الحظر
serversocketchannel = serversocketchannel.open () ؛ serversocketchannel.configureBlocking (false) ؛
2. ربط الاستماع وتكوين معلمات TCP ، مثل حجم التراكم
serversocketchannel.socket ()
3. قم بإنشاء مؤشر ترابط I/O مستقل لاستقصاء محدد الإرسال المضاعف
4. قم بإنشاء محدد ، وتسجيل serversockechannel الذي قمت بإنشائه مسبقًا إلى المحدد ، واستمع إلى SelecteKey.Accept
selector = selector.open () ؛ serversocketchannel.register (selector ، selectekey.op_accept) ؛
5. ابدأ مؤشر ترابط I/O ، وقم بتنفيذ طريقة Selector.Select () في جسم الحلقة ، واستطلاع القناة الجاهزة
على الرغم من أن (True) {try {// select () clocks حتى تكون قناة واحدة على الأقل جاهزة للحدث الذي قمت بتسجيله // إذا لم تكن هناك قناة جاهزة ، فسيتم حظرها هنا // Select (ant timeout) هي نفسها Select () ، باستثناء أنها ستحظر المهلة milliseconds (المعلمات). celector.select () ؛ } catch (ioException e) {// todo acto catch block e.printstacktrace () ؛ استراحة؛ }}6. عند استقصاء القناة في الدولة الجاهزة ، يجب الحكم عليها. إذا كانت حالة OP_ACCEPT ، فهذا يعني أنها وصول جديد للعميل. ثم استدعاء method serversocketchannel.accept () لقبول العميل الجديد.
// إرجاع مفتاح التحديد الجاهز ، ثم تكرار لتنفيذ SET <SelecteKey> readKeys = selector.SelectedKeys () ؛ لـ (iterator <selectekey> it = readkeys.iterator () ؛ it.hasNext () ؛) {selectekey key = it.next () ؛ it.remove () ؛ حاول {if (key.isacceptable ()) {serversocketchannel server = (serversocketchannel) key.channel () ؛ Socketchannel Client = server.accept () ؛ client.configureBlocking (false) ؛ client.register (selector ، selectekey.op_write) ؛ } آخر if (key.iswritable ()) {socketchannel client = (socketchannel) key.channel () ؛ Bytebuffer Buffer = bytebuffer.allocate (20) ؛ String str = "Hello" ؛ buffer = bytebuffer.wrap (str.getBytes ()) ؛ client.write (Buffer) ؛ key.cancel () ؛ }} catch (ioException e) {E.PrintStackTrace () ؛ key.cancel () ؛ حاول {key.channel (). close () ؛ } catch (ioException e1) {// todo acto catch block e1.printstacktrace () ؛ }}}7. قم بتعيين Socketchannel Link Client Link الذي تم الوصول إليه حديثًا على وضع عدم الحظر ، وتكوين بعض معلمات TCP الأخرى.
if (key.isacceptable ()) {serversocketchannel server = (serversocketchannel) key.channel () ؛ Socketchannel Client = server.accept () ؛ client.configureBlocking (false) ؛ ...}8. سجل Socketchannel إلى المحدد والاستماع إلى OP_WRITE
client.register (selector ، selectekey.op_write) ؛
9. إذا كانت القناة التي تم استطلاعها OP_WRITE ، فهذا يعني أن البيانات يجب كتابتها في Sockchannel ، ثم يتم إنشاء كائن Bytebuffer وكتابة حزمة البيانات.
آخر if (key.iswritable ()) {socketchannel client = (socketchannel) key.channel () ؛ Bytebuffer Buffer = bytebuffer.allocate (20) ؛ String str = "Hello" ؛ buffer = bytebuffer.wrap (str.getBytes ()) ؛ client.write (Buffer) ؛ key.cancel () ؛ }الرمز الكامل كما يلي:
استيراد java.io.ioException ؛ استيراد java.net.inetsocketaddress ؛ استيراد java.nio.bytebuffer ؛ استيراد java.nio.channels.selectionkeke ؛ java.nio.channels.socketchannel ؛ import java.util.iterator ؛ استيراد java.util.set ؛ فئة عامة خوادم corversocketchanneldemo {public static void main (string [] args) serversocketchannel.open () ؛ serversocketchannel.configureblocking (false) ؛ serversocketchannel.socket (). blocke.printStackTrace();}while(true) {try {//select() blocks until at least one channel is ready on the event you registered //If there is no channel ready, it will block here all the time.//select(long timeout) is the same as select(), except that it will block timeout milliseconds (parameters). celector.select () ؛} catch (ioException e) {// todo catch catch blocke.printstacktrace () ؛ break ؛} // إرجاع مفتاح التحديد الجاهز ، ثم التكرار لتنفيذ set <selectekey> readkeys = celector.selectedkeys () ؛ for (iterator <sechanekey> headkeys.aterator () key = it.next () ؛ it.remove () ؛ حاول {if (key.isacceptable ()) {serversoCketchAnler server = (serversocketchannel) key.channel () ؛ socketchannel client = server.accept () client = (socketchannel) key.channel () ؛ bytebuffer buffer = bytebuffer.allocate (20) ؛ string str = "hello" ؛ buffer = bytebuffer.wrap (str.getbytes () ؛ client.write (buffer) ؛ key.cancel () ؛ {key.channel (). close () ؛} catch (ioException e1) {// todo catch blocke1.printstacktrace () ؛}}}}}}}}}}}}نستخدم Telnet LocalHost 8080 لمحاكاة عملاء متعددين:
نتائج تشغيل البرنامج كما يلي:
لخص
ما ورد أعلاه هو كل التفسير التفصيلي لتطوير خادم Java Nio في هذه المقالة ، وآمل أن يكون مفيدًا للجميع. يمكن للأصدقاء المهتمين الاستمرار في الرجوع إلى الموضوعات الأخرى ذات الصلة على هذا الموقع. إذا كانت هناك أي أوجه قصور ، فيرجى ترك رسالة لإشارةها. شكرا لك يا أصدقائك لدعمكم لهذا الموقع!