java.io
החבילה המקורית
וגם NIO, java.nio
ממשקי ה- API של I / O ( ) שאינם חוסמים
שהוצגו ב- Java 1.4. לבסוף תראה דוגמה המדגימה את רשתות Java כפי שהיא מיושמת מ- Java 7 קדימה, ב- NIO.2.
תכנות שקעים מסתכם בשתי מערכות המתקשרות זו עם זו. באופן כללי, תקשורת רשת מגיעה בשני טעמים: פרוטוקול בקרת התחבורה (TCP) ופרוטוקול דאטאגרמת המשתמש (UDP). TCP ו- UDP משמשים למטרות שונות ולשניהם מגבלות ייחודיות:
- TCP הוא פרוטוקול פשוט ואמין יחסית המאפשר ללקוח ליצור חיבור לשרת ולשתי המערכות לתקשר. ב- TCP, כל ישות יודעת כי מטעני התקשורת שלה התקבלו.
- UDP הוא פרוטוקול ללא חיבור והוא טוב לתרחישים שבהם אתה לא בהכרח זקוק לכל חבילה כדי להגיע ליעדה, כגון הזרמת מדיה.
כדי להעריך את ההבדל בין TCP ו- UDP, שקול מה היה קורה אם היית מזרים וידאו מהאתר המועדף עליך והוא מפיל מסגרות. האם אתה מעדיף שהלקוח יאט את הסרט שלך כדי לקבל את המסגרות החסרות או שהיית מעדיף שהסרטון ימשיך להתנגן? פרוטוקולי הזרמת וידאו ממנפים בדרך כלל UDP. מכיוון ש- TCP מבטיח מסירה, זהו הפרוטוקול הנבחר עבור HTTP, FTP, SMTP, POP3 וכו '.
במדריך זה אני מציג בפניכם תכנות שקעים בג'אווה. אני מציג סדרה של דוגמאות של שרת לקוחות שמדגימות תכונות ממסגרת ה- I / O המקורית של Java, ואז עוברת בהדרגה לשימוש בתכונות שהוצגו ב- NIO.2.
שקעי ג'אווה מבית הספר הישן
ביישומים שקדמו ל- NIO, קוד שקע לקוח Java TCP מטופל על ידי java.net.Socket
המחלקה. הקוד הבא פותח חיבור לשרת:
שקע שקע = שקע חדש (שרת, יציאה);
ברגע socket
שהמופע שלנו מחובר לשרת אנו יכולים להתחיל להשיג זרמי קלט ופלט לנתק. זרמי קלט משמשים לקריאת נתונים מהשרת ואילו זרמי פלט משמשים לכתיבת נתונים לשרת. אנו יכולים לבצע את השיטות הבאות להשגת זרמי קלט ופלט:
InputStream in = socket.getInputStream (); OutputStream out = socket.getOutputStream ();
מכיוון שמדובר בזרמים רגילים, אותם זרמים בהם נשתמש לקרוא ולכתוב לקובץ, אנו יכולים להמיר אותם לצורה המשרתת בצורה הטובה ביותר את מקרה השימוש שלנו. לדוגמא, נוכל לעטוף את ה- OutputStream
A PrintStream
כך שנוכל לכתוב טקסט בקלות בשיטות כמו println()
. לדוגמא אחרת, נוכל לעטוף את ה- InputStream
A BufferedReader
באמצעות Via InputStreamReader
כדי לקרוא טקסט בקלות בשיטות כמו readLine()
.
דוגמא לקוח שקע Java
בואו נעבור דוגמה קצרה שמבצעת HTTP GET כנגד שרת HTTP. HTTP מתוחכם יותר ממה שהדוגמה שלנו מאפשרת, אך אנו יכולים לכתוב קוד לקוח לטיפול במקרה הפשוט ביותר: בקש משאב מהשרת והשרת מחזיר את התגובה וסוגר את הזרם. מקרה זה דורש את השלבים הבאים:
- צור שקע לשרת האינטרנט המאזין ביציאה 80.
- השג א
PrintStream
לשרת ושלח את הבקשהGET PATH HTTP/1.0
, היכןPATH
נמצא המשאב המבוקש בשרת. לדוגמא, אם היינו רוצים לפתוח את השורש של אתר אינטרנט, הנתיב יהיה/
. - השג
InputStream
לשרת, עוטף אותו עם aBufferedReader
וקרא את התגובה שורה אחר שורה.
רישום 1 מציג את קוד המקור לדוגמא זו.
רישום 1. SimpleSocketClientExample.java
חבילה com.geekcap.javaworld.simplesocketclient; ייבא java.io.BufferedReader; ייבא java.io.InputStreamReader; ייבא java.io.PrintStream; ייבא java.net.Socket; class class SimpleSocketClientExample {public static void main (String [] args) {if (args.length <2) {System.out.println ("Usage: SimpleSocketClientExample"); System.exit (0); } שרת מחרוזות = טענות [0]; נתיב מחרוזת = טענות [1]; System.out.println ("טוען תוכן של כתובת אתר:" + שרת); נסה {// התחבר לשרת שקע שקע = שקע חדש (שרת, 80); // צור זרמי קלט ופלט לקריאה וכתיבה לשרת PrintStream out = PrintStream חדש (socket.getOutputStream ()); BufferedReader in = BufferedReader חדש (InputStreamReader חדש (socket.getInputStream ())); // עקוב אחר פרוטוקול HTTP של GET HTTP / 1.0 ואחריו שורה ריקה outprint.println ("GET" + נתיב + "HTTP / 1.0"); out.println (); // קרא נתונים מהשרת עד שנסיים לקרוא את המסמך String line = in.readLine (); בעוד (קו! = null) {System.out.println (קו); קו = in.readLine (); } // סגור את הזרמים שלנו ב- .close (); out.close (); socket.close (); } לתפוס (חריג e) {e.printStackTrace (); }}}
רישום 1 מקבל שני ארגומנטים בשורת הפקודה: השרת שאליו יש להתחבר (בהנחה שאנחנו מתחברים לשרת ביציאה 80) והמשאב לאחזור. זה יוצר A Socket
שמצביע על השרת ומציין במפורש את היציאה 80
. לאחר מכן הוא מבצע את הפקודה:
קבל PATH HTTP / 1.0
לדוגמה:
GET / HTTP / 1.0
מה קרה הרגע?
כאשר אתה מאחזר דף אינטרנט משרת אינטרנט, כגון www.google.com
, לקוח HTTP משתמש בשרתי DNS כדי למצוא את כתובת השרת: זה מתחיל בבקשה משרת הדומיין ברמה העליונה את com
התחום שבו שרת שמות הדומיין הסמכותי הוא עבור www.google.com
. ואז הוא מבקש ששרת שם הדומיין עבור כתובת ה- IP (או הכתובות) עבור www.google.com
. לאחר מכן הוא פותח שקע לשרת זה ביציאה 80. (לחלופין, אם ברצונך להגדיר יציאה אחרת, תוכל לעשות זאת על ידי הוספת נקודתיים ולאחריה מספר היציאה, למשל:. :8080
) לבסוף, לקוח HTTP מבצע והמתודה שצוין, כגון GET
, POST
, PUT
, DELETE
, HEAD
, או OPTI/ONS
. לכל שיטה תחביר משלה. כפי שמוצג בתצלומי הקוד לעיל, GET
השיטה דורשת נתיב ואחריוHTTP/version number
ושורה ריקה. אם היינו רוצים להוסיף כותרות HTTP היינו יכולים לעשות זאת לפני שנכנס לשורה החדשה.
ברשימה 1 שלפנו את האריזה OutputStream
ועטפנו אותה PrintStream
כך שנוכל לבצע את הפקודות מבוססות הטקסט שלנו ביתר קלות. הקוד שלנו קיבל InputStream
, עטף את זה ב- an InputStreamReader
, שהמיר אותו ל- a Reader
, ואז עטף את זה ב- a BufferedReader
. השתמשנו ב- PrintStream
כדי לבצע את GET
השיטה שלנו ואז השתמשנו BufferedReader
בכדי לקרוא את התגובה שורה אחר שורה עד שקיבלנו null
תגובה, מה שמעיד על כך שהשקע היה סגור.
עכשיו בצע את המעמד הזה והעביר לו את הטיעונים הבאים:
java com.geekcap.javaworld.simplesocketclient.SimpleSocketClient דוגמה www.javaworld.com /
אתה אמור לראות פלט דומה למה שלמטה:
טוען תוכן של כתובת האתר: www.javaworld.com HTTP / 1.1 200 אישור תאריך: א ', 21 בספטמבר 2014 22:20:13 GMT שרת: Apache X-Gas_TTL: 10 Cache-Control: max-age = 10 X-GasHost: gas2 .usw X- בישול-עם: בנזין-מקומי X-בנזין-גיל: 8 אורך תוכן: 168 שונה לאחרונה: יום שלישי, 24 בינואר 2012 00:09:09 GMT תג: "60001b-a8-4b73af4bf3340" סוג תוכן : text / html משתנה: חיבור קבלת קידוד: סגור דף בדיקת בנזיןהַצלָחָה
פלט זה מציג דף בדיקה באתר JavaWorld. זה השיב בחזרה שהוא מדבר ב- HTTP גרסה 1.1 והתגובה היא 200 OK
.
דוגמה לשרת שקעי Java
כיסינו את צד הלקוח ולמרבה המזל היבט התקשורת של צד השרת הוא קל באותה מידה. מנקודת מבט פשטנית, התהליך הוא כדלקמן:
- צור a
ServerSocket
, וציין יציאה להאזנה. - להפעיל את
ServerSocket
ים"accept()
שיטת להקשיב על הנמל המוגדר לחיבור הלקוח. - כאשר לקוח מתחבר לשרת,
accept()
השיטה מחזירהSocket
דרכה השרת יכול לתקשר עם הלקוח. זה אותוSocket
מעמד בו השתמשנו עבור הלקוח שלנו, ולכן התהליך זהה: להשיגInputStream
קריאה מהלקוחOutputStream
וכתיבה ללקוח. - אם השרת שלך צריך להיות מדרגי, תרצה להעביר את
Socket
השרשור לתהליך אחר כדי שהשרת שלך יוכל להמשיך להאזין לחיבורים נוספים. - התקשר
ServerSocket
ים"accept()
שיטה שוב להקשיב לחיבור אחר.
כפי שתראו בקרוב, הטיפול ב- NIO בתרחיש זה יהיה שונה במקצת. לעת עתה, עם זאת, אנו יכולים ליצור ישירות א על ServerSocket
ידי העברת יציאה להאזנה (עוד על ServerSocketFactory
s בפרק הבא):
ServerSocket serverSocket = ServerSocket חדש (יציאה);
ועכשיו נוכל לקבל חיבורים נכנסים באמצעות accept()
השיטה:
שקע שקע = serverSocket.accept (); // לטפל בחיבור ...
תכנות רב-הברגה עם שקעי Java
רישום 2, להלן, מחבר את כל קוד השרת עד כה לדוגמה מעט חזקה יותר המשתמשת בשרשור לטיפול בכמה בקשות. השרת המוצג הוא שרת הד , כלומר הוא מהדהד כל הודעה שהוא מקבל.
While the example in Listing 2 isn't complicated it does anticipate some of what's coming up in the next section on NIO. Pay special attention to the amount of threading code we have to write in order to build a server that can handle multiple simultaneous requests.
Listing 2. SimpleSocketServer.java
package com.geekcap.javaworld.simplesocketclient; import java.io.BufferedReader; import java.io.I/OException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class SimpleSocketServer extends Thread { private ServerSocket serverSocket; private int port; private boolean running = false; public SimpleSocketServer( int port ) { this.port = port; } public void startServer() { try { serverSocket = new ServerSocket( port ); this.start(); } catch (I/OException e) { e.printStackTrace(); } } public void stopServer() { running = false; this.interrupt(); } @Override public void run() { running = true; while( running ) { try { System.out.println( "Listening for a connection" ); // Call accept() to receive the next connection Socket socket = serverSocket.accept(); // Pass the socket to the RequestHandler thread for processing RequestHandler requestHandler = new RequestHandler( socket ); requestHandler.start(); } catch (I/OException e) { e.printStackTrace(); } } } public static void main( String[] args ) { if( args.length == 0 ) { System.out.println( "Usage: SimpleSocketServer " ); System.exit( 0 ); } int port = Integer.parseInt( args[ 0 ] ); System.out.println( "Start server on port: " + port ); SimpleSocketServer server = new SimpleSocketServer( port ); server.startServer(); // Automatically shutdown in 1 minute try { Thread.sleep( 60000 ); } catch( Exception e ) { e.printStackTrace(); } server.stopServer(); } } class RequestHandler extends Thread { private Socket socket; RequestHandler( Socket socket ) { this.socket = socket; } @Override public void run() { try { System.out.println( "Received a connection" ); // Get input and output streams BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream() ) ); PrintWriter out = new PrintWriter( socket.getOutputStream() ); // Write out our header to the client out.println( "Echo Server 1.0" ); out.flush(); // Echo lines back to the client until the client closes the connection or we receive an empty line String line = in.readLine(); while( line != null && line.length() > 0 ) { out.println( "Echo: " + line ); out.flush(); line = in.readLine(); } // Close our connection in.close(); out.close(); socket.close(); System.out.println( "Connection closed" ); } catch( Exception e ) { e.printStackTrace(); } } }