כתיבת קוד המבוצע במכשיר מסוים מספקת מאוד. אבל, כתיבת קוד המבוצע במספר מכשירים המתקשרים זה עם זה היא פשוט מאשרת חיים. מאמר זה ילמד אותך כיצד לחבר ולהחליף הודעות ברשת באמצעות פרוטוקול בקרת שידור (TCP).
במאמר זה תגדיר אפליקציה שתחבר את המחשב שלך לעצמו ובעצם תעשה אותו מטורף - דבר עם עצמו. תלמד גם את ההבדל בין שני הזרמים הנפוצים ביותר לרשת ב- Java וכיצד הם פועלים.
זרמי נתונים ואובייקטים
לפני הצלילה לקוד, יש להבחין בהבדל בין שני הזרמים המשמשים במאמר.
זרמי נתונים
זרמי נתונים מעבדים סוגי נתונים ומחרוזות פרימיטיביות. הנתונים הנשלחים באמצעות זרמי נתונים צריכים להיות מסודרים באופן ידני ומסוגרים לאתר, מה שמקשה על העברת נתונים מורכבים. אבל, זרמי נתונים יכולים לתקשר עם שרתים ולקוחות הכתובים בשפות אחרות מאשר Java. זרמי גלם דומים לזרמי נתונים בהיבט זה, אך זרמי נתונים מבטיחים שהפורמט של הנתונים הוא באופן עצמאי בפלטפורמה וזה מועיל מכיוון ששני הצדדים יוכלו לקרוא נתונים שנשלחו.
זרמי אובייקטים
זרמי אובייקטים מעבדים סוגי נתונים פרימיטיביים ואובייקטים המיישמים
ניתן לעריכה
מִמְשָׁק. הנתונים הנשלחים על פני זרמי אובייקטים מסודרים ומסודרים באופן אוטומטי, מה שמקל על העברת נתונים מורכבים. אבל, זרמי אובייקטים יכולים לתקשר רק עם שרתים ולקוחות שנכתבו ב- Java. גַם,
ObjectOutputStream
עם האתחול, שולח כותרת אל
InputStream
של הצד השני אשר, בעת האתחול, חוסם את הביצוע עד לקבלת הכותרת.
צעדים
שלב 1. צור כיתה
צור כיתה ושם אותה כרצונך. במאמר זה הוא ייקרא בשם
דוגמא NetworkApp
מחלקה ציבורית NetworkAppExample {}
שלב 2. צור שיטה עיקרית
צור שיטה עיקרית והצהיר שהיא עשויה לזרוק חריגים מ-
יוצא מן הכלל
סוג וכל תת -סוג שלו - כל היוצאים מן הכלל. זה נחשב לפרקטיקה גרועה, אך מקובל על דוגמאות עצמות.
מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {}}
שלב 3. הצהרת כתובת השרת
דוגמה זו תשתמש בכתובת מארח מקומית ובמספר יציאה שרירותי. מספר הנמל צריך להיות בטווח שבין 0 ל- 65535 (כולל). עם זאת, מספרי יציאות שיש להימנע מהם נעים בין 0 ל- 1023 (כולל) מכיוון שהם יציאות מערכת שמורות.
מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; }}
שלב 4. צור שרת
השרת כבול לכתובת והיציאה ומאזין לחיבורים נכנסים. בג'אווה,
ServerSocket
מייצג נקודת קצה בצד השרת ותפקידה קבלת חיבורים חדשים.
ServerSocket
אין זרמים לקריאה ושליחת נתונים מכיוון שאינו מייצג חיבור בין שרת ללקוח.
יבוא java.net. InetAddress; יבוא java.net. ServerSocket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); }}
שלב 5. הקמת שרת יומן
לצורכי רישום, הדפס למסוף שהשרת הופעל.
יבוא java.net. InetAddress; יבוא java.net. ServerSocket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); }}
שלב 6. צור לקוח
הלקוח כבול לכתובת וליציאה של השרת ומאזין למנות (הודעות) לאחר יצירת החיבור. בג'אווה,
שֶׁקַע
מייצג נקודת קצה בצד הלקוח המחובר לשרת או חיבור (מהשרת) ללקוח ומשמש לתקשר עם הצד בצד השני.
יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); }}
שלב 7. ניסיון חיבור יומן
לצורכי רישום, הדפס למסוף כי ניסה חיבור.
יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); }}
שלב 8. יצירת חיבור
לקוחות לעולם לא יתחברו אלא אם השרת מקשיב ומקבל, במילים אחרות יוצר, חיבורים. ב- Java, נוצרים חיבורים באמצעות
לְקַבֵּל()
שיטה של
ServerSocket
מעמד. השיטה תחסום את הביצוע עד שהלקוח יתחבר.
יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); }}
שלב 9. חיבור נוצר ביומן
לצורכי רישום, הדפס למסוף כי נוצר חיבור בין השרת ללקוח.
יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); }}
שלב 10. הכן זרמי תקשורת
התקשורת מתבצעת באמצעות זרמים, וביישום זה, יש לחבר זרמים גולמיים של (חיבור מ) השרת (ללקוח) והלקוח לנתוני או לזרמי אובייקטים. זכור, שני הצדדים צריכים להשתמש באותו סוג זרם.
-
זרמי נתונים
יבוא java.io. DataInputStream; יבוא java.io. DataOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); DataOutputStream clientOut = DataOutputStream חדש (client.getOutputStream ()); DataInputStream clientIn = DataInputStream חדש (client.getInputStream ()); DataOutputStream serverOut = DataOutputStream חדש (connection.getOutputStream ()); DataInputStream serverIn = DataInputStream חדש (connection.getInputStream ()); }}
-
זרמי אובייקטים
כאשר משתמשים במספר זרמי אובייקטים, יש לאתחל זרמי קלט באותו סדר כמו זרמי פלט מכיוון
ObjectOutputStream
שולח כותרת לצד השני ו
ObjectInputStream
חוסם את הביצוע עד שהוא קורא את הכותרת.
יבוא java.io. ObjectInputStream; יבוא java.io. ObjectOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); ObjectOutputStream clientOut = חדש ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = חדש ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = חדש ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = חדש ObjectInputStream (connection.getInputStream ()); }}
ההזמנה כמפורט בקוד לעיל עשויה להיות קלה יותר לזכירה - תחילה אתחל זרמי פלט ולאחר מכן הזן זרמים באותו סדר. עם זאת, צו נוסף לאתחול של זרמי אובייקטים הוא כדלקמן:
ObjectOutputStream clientOut = חדש ObjectOutputStream (client.getOutputStream ()); ObjectInputStream serverIn = חדש ObjectInputStream (connection.getInputStream ()); ObjectOutputStream serverOut = חדש ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = חדש ObjectInputStream (client.getInputStream ());
שלב 11. רשום שהתקשורת מוכנה
לצורכי רישום, הדפס לקונסולה שהתקשורת מוכנה.
// קוד השמיט את הייבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); // קוד השמיט System.out.println ("התקשורת מוכנה."); }}
שלב 12. צור הודעה
ביישום זה,
שלום עולם
הטקסט יישלח לשרת או כ
בייט
אוֹ
חוּט
. הכריז על משתנה מהסוג שתלוי בזרם המשמש. להשתמש
בייט
לזרמי נתונים ו
חוּט
עבור זרמי אובייקטים.
-
זרמי נתונים
באמצעות זרמי נתונים, הסדרה נעשית על ידי המרת אובייקטים לסוגי נתונים פרימיטיביים או א
חוּט
. במקרה הזה,
חוּט
מומרת ל
בייט
במקום לכתוב באמצעות
writeBytes ()
שיטה להראות כיצד זה יתבצע עם אובייקטים אחרים, כגון תמונות או קבצים אחרים.
יבוא java.io. DataInputStream; יבוא java.io. DataOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); DataOutputStream clientOut = DataOutputStream חדש (client.getOutputStream ()); DataInputStream clientIn = DataInputStream חדש (client.getInputStream ()); DataOutputStream serverOut = DataOutputStream חדש (connection.getOutputStream ()); DataInputStream serverIn = DataInputStream חדש (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); byte messageOut = "שלום עולם".getBytes (); }}
-
זרמי אובייקטים
יבוא java.io. ObjectInputStream; יבוא java.io. ObjectOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); ObjectOutputStream clientOut = חדש ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = חדש ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = חדש ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = חדש ObjectInputStream (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); String messageOut = "שלום עולם"; }}
שלב 13. שלח את ההודעה
כתוב נתונים לזרם הפלט ושטף את הזרם כדי לוודא שהנתונים נכתבו במלואם.
-
זרמי נתונים
אורך ההודעה צריך להישלח תחילה כדי שהצד השני יידע כמה בתים הוא צריך לקרוא. לאחר שליחת האורך כסוג שלם פרימיטיבי, ניתן לשלוח בתים.
יבוא java.io. DataInputStream; יבוא java.io. DataOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); DataOutputStream clientOut = DataOutputStream חדש (client.getOutputStream ()); DataInputStream clientIn = DataInputStream חדש (client.getInputStream ()); DataOutputStream serverOut = DataOutputStream חדש (connection.getOutputStream ()); DataInputStream serverIn = DataInputStream חדש (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); byte messageOut = "שלום עולם".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); }}
-
זרמי אובייקטים
יבוא java.io. ObjectInputStream; יבוא java.io. ObjectOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); ObjectOutputStream clientOut = חדש ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = חדש ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = חדש ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = חדש ObjectInputStream (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); String messageOut = "שלום עולם"; clientOut.writeObject (messageOut); clientOut.flush (); }}
שלב 14. הודעה שנשלחה ביומן
לצורכי רישום, הדפס למסוף את ההודעה שנשלחה.
-
זרמי נתונים
יבוא java.io. DataInputStream; יבוא java.io. DataOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); DataOutputStream clientOut = DataOutputStream חדש (client.getOutputStream ()); DataInputStream clientIn = DataInputStream חדש (client.getInputStream ()); DataOutputStream serverOut = DataOutputStream חדש (connection.getOutputStream ()); DataInputStream serverIn = DataInputStream חדש (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); byte messageOut = "שלום עולם".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("הודעה נשלחה לשרת:" + מחרוזת חדשה (messageOut)); }}
-
זרמי אובייקטים
יבוא java.io. ObjectInputStream; יבוא java.io. ObjectOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); ObjectOutputStream clientOut = חדש ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = חדש ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = חדש ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = חדש ObjectInputStream (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); String messageOut = "שלום עולם"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("הודעה נשלחה לשרת:" + messageOut); }}
שלב 15. קרא את ההודעה
קרא נתונים מזרם הקלט והמיר אותם. מכיוון שאנו יודעים בדיוק את סוג הנתונים שנשלחו, ניצור או
חוּט
מ
בייט
או ליהוק
לְהִתְנַגֵד
ל
חוּט
ללא בדיקה, תלוי בזרם המשמש.
-
זרמי נתונים
מכיוון שהאורך נשלח תחילה ובייטים לאחר מכן, הקריאה צריכה להיעשות באותו סדר. במקרה שאורך הוא אפס, אין מה לקרוא. אובייקט מנוטרל כאשר בתים מומרים חזרה למופע, במקרה זה, של
חוּט
יבוא java.io. DataInputStream; יבוא java.io. DataOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); DataOutputStream clientOut = DataOutputStream חדש (client.getOutputStream ()); DataInputStream clientIn = DataInputStream חדש (client.getInputStream ()); DataOutputStream serverOut = DataOutputStream חדש (connection.getOutputStream ()); DataInputStream serverIn = DataInputStream חדש (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); byte messageOut = "שלום עולם".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("הודעה נשלחה לשרת:" + מחרוזת חדשה (messageOut)); int length = serverIn.readInt (); if (אורך> 0) {בייט messageIn = בייט חדש [אורך]; serverIn.readFully (messageIn, 0, messageIn.length); }}}
-
זרמי אובייקטים
יבוא java.io. ObjectInputStream; יבוא java.io. ObjectOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); ObjectOutputStream clientOut = חדש ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = חדש ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = חדש ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = חדש ObjectInputStream (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); String messageOut = "שלום עולם"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("הודעה נשלחה לשרת:" + messageOut); String messageIn = (String) serverIn.readObject (); }}
שלב 16. הודעת קריאת יומן
לצורכי רישום, הדפס למסוף שההודעה התקבלה והדפס את תוכנו.
-
זרמי נתונים
יבוא java.io. DataInputStream; יבוא java.io. DataOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); DataOutputStream clientOut = DataOutputStream חדש (client.getOutputStream ()); DataInputStream clientIn = DataInputStream חדש (client.getInputStream ()); DataOutputStream serverOut = DataOutputStream חדש (connection.getOutputStream ()); DataInputStream serverIn = DataInputStream חדש (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); byte messageOut = "שלום עולם".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("הודעה נשלחה לשרת:" + מחרוזת חדשה (messageOut)); int length = serverIn.readInt (); if (אורך> 0) {בייט messageIn = בייט חדש [אורך]; serverIn.readFully (messageIn, 0, messageIn.length); System.out.println ("הודעה שהתקבלה מהלקוח:" + מחרוזת חדשה (messageIn)); }}}
-
זרמי אובייקטים
יבוא java.io. ObjectInputStream; יבוא java.io. ObjectOutputStream; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); ObjectOutputStream clientOut = חדש ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = חדש ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = חדש ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = חדש ObjectInputStream (connection.getInputStream ()); System.out.println ("התקשורת מוכנה."); String messageOut = "שלום עולם"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("הודעה נשלחה לשרת:" + messageOut); String messageIn = (String) serverIn.readObject (); System.out.println ("הודעה שהתקבלה מהלקוח:" + messageIn); }}
שלב 17. נתק חיבורים
החיבור מנותק כאשר צד אחד סוגר את הזרמים שלו. בג'אווה, על ידי סגירת זרם הפלט, גם השקע והזרם הקלט הנלווים נסגרים. ברגע שצד בצד השני מגלה שהחיבור מת, הוא צריך לסגור גם את זרם הפלט שלו, כדי למנוע דליפות זיכרון.
// קוד השמיט את הייבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); // קוד השמיט System.out.println ("התקשורת מוכנה."); // קוד השמיט clientOut.close (); serverOut.close (); }}
שלב 18. ניתוק יומן
לצורכי רישום, חיבור ההדפסה למסוף נותק.
// קוד השמיט את הייבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); // קוד השמיט System.out.println ("התקשורת מוכנה."); // קוד השמיט clientOut.close (); serverOut.close (); System.out.println ("חיבורים נסגרו."); }}
שלב 19. הפסקת השרת
החיבורים מנותקים, אך השרת עדיין פועל. כפי ש
ServerSocket
אינו קשור לשום זרם, יש לסגור אותו במפורש על ידי התקשרות
סגור()
שיטה.
// קוד השמיט את הייבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); // קוד השמיט System.out.println ("התקשורת מוכנה."); // קוד השמיט clientOut.close (); serverOut.close (); System.out.println ("חיבורים נסגרו."); server.close (); }}
שלב 20. סיום שרת יומן
לצורכי רישום, ההדפסה לשרת המסוף הופסקה.
// קוד השמיט את הייבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. Socket; מחלקה ציבורית NetworkAppExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת ServerSocket = ServerSocket חדש (יציאה, 50, InetAddress.getByName (מארח)); System.out.println ("השרת הופעל."); לקוח Socket = Socket חדש (מארח, יציאה); System.out.println ("מתחבר לשרת …"); חיבור Socket = server.accept (); System.out.println ("נוצר חיבור."); // קוד השמיט System.out.println ("התקשורת מוכנה."); // קוד השמיט clientOut.close (); serverOut.close (); System.out.println ("חיבורים נסגרו."); server.close (); System.out.println ("השרת הסתיים."); }}
שלב 21. הידור והפעל
הרישום איפשר לנו לדעת אם היישום מצליח או לא. תפוקה צפויה:
השרת הופעל. מתחבר לשרת … נוצר חיבור. התקשורת מוכנה. ההודעה נשלחה לשרת: הודעת הוול וורלד התקבלה מהלקוח: חיבורי הוול עולם נסגרו. השרת הסתיים.
במקרה שהפלט שלך אינו דומה לזה שלמעלה, וזה לא סביר שיקרה, ישנם מספר פתרונות:
-
אם הפלט עוצר בקו
נוצר חיבור.
ונחלי אובייקט משמשים, שוטפים כל אחד
ObjectOutputStream
- מיד לאחר האתחול מכיוון שכותרות, מסיבה כלשהי, לא נשלחו.
-
אם הפלט מודפס
java.net. BindException: כתובת כבר בשימוש
- בחר מספר יציאה אחר מכיוון שכבר נעשה שימוש במספר שצוין.
טיפים
- התחברות לשרת ברשת אחרת מתבצעת על ידי חיבור לכתובת ה- IP החיצונית של התקן שמריץ את השרת בעל יציאה מועברת.
- החיבור לשרת באותה רשת מתבצע על ידי חיבור לכתובת ה- IP הפרטית של התקן שמריץ את השרת או העברת יציאה וחיבור לכתובת ה- IP החיצונית של המכשיר.
- ישנן תוכנות, כגון Hamachi, המאפשרות התחברות לשרת ברשת אחרת מבלי להעביר יציאה, אך היא דורשת התקנת התוכנה בשני המכשירים.
דוגמאות
יישומי רשת המשתמשים בחסימת קלט/פלט צריכים להשתמש בשרשורים. הדוגמאות הבאות מציגות יישום שרת ולקוח מינימליסטי עם שרשורים. קוד רשת זהה למעשה למאמר, למעט שחלק מהקטעים סונכרנו, הועברו לאשכולות ומטופלים בחריגים.
Server.java
יבוא java.io. IOException; יבוא java.net. InetAddress; יבוא java.net. ServerSocket; יבוא java.net. SocketException; יבוא java.net. UnknownHostException; יבוא java.util. ArrayList; יבוא java.util. Collections; יבוא java.util. List; /*** המחלקה {@code Server} מייצגת נקודת סיום של שרת ברשת. {@code Server} נקשר פעם לכתובת IP מסוימת ויציאה מסוימת, יוצר קשרים עם לקוחות ומסוגל לתקשר איתם או לנתק אותם. *
* מחלקה זו בטוחה לכל חוטים. * * @version 1.0 * @see לקוח * @see חיבור */ שרת ברמה ציבורית מיישם Runnable {server ServerSocket פרטי; רשימה פרטית
קשרים; חוט חוט פרטי; פרטי אובייקט סופי חיבורים Lock = new Object (); /** * בונה {@code Server} המתקשר עם לקוחות על שם המארח והיציאה שצוין עם אורך המקסימום * המבוקש של תור של לקוחות נכנסים. * * @param מארח כתובת מארח לשימוש. * מספר יציאת @param לשימוש. * צבר @param ביקש אורך מקסימלי של התור של לקוחות נכנסים. * @זורק NetworkException אם מתרחשת שגיאה בעת הפעלת שרת. */ שרת ציבורי (מארח מחרוזת, יציאת int, צבר int) זורק NetworkException {try {server = new ServerSocket (יציאה, צבר, InetAddress.getByName (מארח)); } catch (UnknownHostException e) {throw new NetworkException ("לא ניתן לפתור שם מארח:" + מארח, ה); } catch (IllegalArgumentException e) {לזרוק רשת NetworkException חדשה ("מספר הנמל צריך להיות בין 0 ל- 65535 (כולל):" + יציאה); } catch (IOException e) {throw new NetworkException new ("לא ניתן היה להפעיל שרת.", ה); } חיבורים = Collections.synchronizedList (new ArrayList ()); thread = new Thread (זה); thread.start (); } /*** בונה {@code Server} המתקשר עם לקוחות על שם המארח והיציאה שצוינו. * * @param מארח כתובת מארח לאגד. * מספר יציאת @param לכריכה. * @זורק NetworkException אם מתרחשות שגיאות בעת הפעלת שרת. */ שרת ציבורי (מארח מחרוזת, יציאת אינט) זורק NetworkException {זה (מארח, יציאה, 50); } /*** מקשיב, מקבל ורושם חיבורים נכנסים מלקוחות. */ @ביטול הפעלה חלל ציבורי () {while (! Server.isClosed ()) {try {connections.add (חיבור חדש (server.accept ())); } catch (SocketException e) {if (! e.getMessage (). שווה ("Socket סגור")) {e.printStackTrace (); }} catch (NetworkException | IOException e) {e.printStackTrace (); }}} /*** שולח נתונים לכל הלקוחות הרשומים. * * נתוני @param נתוני שליחה. * @throws IllegalStateException אם מנסים לכתוב נתונים כשהשרת אינו מקוון. * @throws IllegalArgumentException אם הנתונים שיש לשלוח הם בטלים. */ שידור חלל ציבורי (נתוני אובייקט) {if (server.isClosed ()) {לזרוק חדש IllegalStateException ("הנתונים לא נשלחו, השרת לא מקוון."); } if (data == null) {throw new IllegalArgumentException ("data null"); } מסונכרן (connectionsLock) {עבור (חיבור חיבור: חיבורים) {נסה {connection.send (נתונים); System.out.println ("הנתונים נשלחו ללקוח בהצלחה."); } catch (NetworkException e) {e.printStackTrace (); }}}} /*** שולח הודעת ניתוק ומנתק את הלקוח שצוין. * * חיבור @param הלקוח מנתק. * @זורק NetworkException אם מתרחשת שגיאה בעת סגירת החיבור. */ ניתוק חלל ציבורי (חיבור חיבור) זורק NetworkException {if (connections.remove (חיבור)) {connection.close (); }} /*** שולח הודעת ניתוק לכל הלקוחות, מנתק אותם ומסיים את השרת. */ public void close () זורק NetworkException {מסונכרן (connectionsLock) {עבור (חיבור חיבור: חיבורים) {נסה {connection.close (); } catch (NetworkException e) {e.printStackTrace (); }}} connections.clear (); נסה {server.close (); } catch (IOException e) {throw new NetworkException ("שגיאה בעת סגירת השרת."); } סוף סוף {thread.interrupt (); }} /*** מחזיר אם השרת מחובר או לא. * * @return נכון אם השרת מחובר. שקר, אחרת. */ public boolean isOnline () {return! server.isClosed (); } /*** מחזירה מערך של לקוחות רשומים. */ חיבור ציבורי getConnections () {מסונכרן (connectionsLock) {החזר חיבורים.טואירי (חיבור חדש [חיבורים. גודל ()]); }}}
Client.java
יבוא java.io. IOException; יבוא java.net. Socket; יבוא java.net. UnknownHostException; /*** המחלקה {@code Client} מייצגת נקודת סיום של לקוח ברשת. מובטח כי {@code Client}, לאחר שהתחבר לשרת מסוים *, יוכל לתקשר רק עם השרת. אם לקוחות אחרים מקבלים את הנתונים * או לא, תלוי ביישום השרת. *
* מחלקה זו הינה בטוחה לאשכול. * * @version 1.0 * @see Server * @see Connection */ Class class Client {חיבור פרטי לחיבור; /*** בונה {@code Client} המחובר לשרת על המארח והיציאה שצוינו. * * @param מארח כתובת מארח לאגד. * מספר יציאת @param לכריכה. * @זורק NetworkException אם מתרחשת שגיאה בעת הפעלת שרת. */ לקוח ציבורי (מארח מחרוזת, יציאת אינט) זורק NetworkException {try {חיבור = חיבור חדש (Socket חדש (מארח, יציאה)); } catch (UnknownHostException e) {throw new NetworkException ("לא ניתן לפתור את שם המארח:" + מארח, ה); } catch (IllegalArgumentException e) {לזרוק רשת NetworkException חדשה ("מספר הנמל צריך להיות בין 0 ל- 65535 (כולל):" + יציאה); } catch (IOException e) {throw new NetworkException new ("לא ניתן היה להפעיל שרת.", ה); }} /*** שולח נתונים לצד השני. * * נתוני @param נתוני שליחה. * @זורק NetworkException אם הכתיבה לזרם הפלט נכשלת. * @throws IllegalStateException אם מנסים לכתוב נתונים כאשר החיבור נסגר. * @throws IllegalArgumentException אם הנתונים שיש לשלוח הם בטלים. * @throws UnsupportedOperationException אם מנסים לשלוח סוג נתונים שאינו נתמך. */ שליחת חלל ציבורי (נתוני אובייקט) זורקת NetworkException {connection.send (נתונים); } /*** שולח הודעת ניתוק לשרת וסוגר אותו עם השרת. */ public void close () זורק NetworkException {connection.close (); } /*** מחזירה אם הלקוח מחובר לשרת או לא. * * @return נכון אם הלקוח מחובר. שקר, אחרת. */ public boolean isOnline () {return return.isConnected (); } /*** מחזירה את מופע {@link Connection} של הלקוח. */ חיבור ציבורי getConnection () {חיבור חזרה; }}
חיבור. Java
יבוא java.io. DataInputStream; יבוא java.io. DataOutputStream; יבוא java.io. IOException; יבוא java.net. Socket; יבוא java.net. SocketException; /** * המחלקה {@code Connection} מייצגת חיבור בין השרת ללקוח או נקודת סיום של לקוח ברשת * {@code Connection}, לאחר חיבורו, הוא מסוגל להחליף נתונים עם צד או צדדים אחרים, בהתאם על יישום שרת *. *
* מחלקה זו הינה בטוחה לאשכול. * * @version 1.0 * @see Server * @see Client */ מחלקה ציבורית חיבור מיישם Runnable {שקע Socket פרטי; DataOutputStream פרטי החוצה; DataInputStream פרטי ב-; חוט חוט פרטי; אובייקט סופי אובייקט writeLock = new Object (); אובייקט סופי פרטי readLock = New Object (); /*** בונה את {@code Connection} באמצעות זרמים של {@link Socket} שצוין. * * שקע @param לשאוף ממנו.*/ חיבור ציבורי (שקע Socket) זורק NetworkException {if (socket == null) {לזרוק חדש IllegalArgumentException ("שקע null"); } this.socket = socket; נסה {out = new DataOutputStream (socket.getOutputStream ()); } catch (IOException e) {throw new NetworkException new ("לא ניתן היה להיכנס לזרם הפלט.", ה); } נסה {in = DataInputStream חדש (socket.getInputStream ()); } catch (IOException e) {throw new NetworkException new ("לא ניתן היה להיכנס לזרם הקלט.", ה); } שרשור = שרשור חדש (זה); thread.start (); } /*** קורא הודעות בזמן שהקשר עם הצד השני חי. */ @ביטול הפעלה ציבורית של ריק () {while (! Socket.isClosed ()) {try {מזהה int; בתים בתים; מסונכרן (readLock) {identifier = in.readInt (); int length = in.readInt (); אם (אורך> 0) {בתים = בתים חדשים [אורך]; in.readFully (בתים, 0, bytes.length); } אחר {המשך; }} switch (מזהה) {case Identifier. INTERNAL: פקודת מחרוזת = מחרוזת חדשה (בתים); if (command.equals ("נתק")) {if (! socket.isClosed ()) {System.out.println ("התקבלה חבילת ניתוק."); נסה {close (); } catch (NetworkException e) {return; } } } לשבור; case Identifier. TEXT: System.out.println ("התקבלה הודעה:" + מחרוזת חדשה (בתים)); לשבור; ברירת מחדל: System.out.println ("התקבלו נתונים לא מזוהים."); }} catch (SocketException e) {if (! e.getMessage (). שווה ("Socket סגור")) {e.printStackTrace (); }} catch (IOException e) {e.printStackTrace (); }}} /*** שולח נתונים לצד השני. * * נתוני @param נתוני שליחה. * @זורק NetworkException אם הכתיבה לזרם הפלט נכשלת. * @throws IllegalStateException אם מנסים לכתוב נתונים כאשר החיבור נסגר. * @throws IllegalArgumentException אם הנתונים שיש לשלוח הם בטלים. * @throws UnsupportedOperationException אם מנסים לשלוח סוג נתונים שאינו נתמך. */ שליחת חלל ציבורי (נתוני אובייקט) זורקת NetworkException {if (socket.isClosed ()) {לזרוק IllegalStateException חדש ("הנתונים לא נשלחו, החיבור נסגר."); } if (data == null) {throw new IllegalArgumentException ("data null"); } מזהה int; בתים בתים; if (data instanceof String) {identifier = Identifier. TEXT; בתים = ((מחרוזת) נתונים).getBytes (); } else {throw new UnsupportedOperationException new ("סוג נתונים לא נתמך:" + data.getClass ()); } נסה {מסונכרן (writeLock) {out.writeInt (מזהה); out.writeInt (bytes.length); out.write (בתים); out.flush (); }} catch (IOException e) {throw new NetworkException new ("לא ניתן היה לשלוח נתונים.", ה); }} /*** שולח הודעת ניתוק אל הצד השני וסוגר אותו. */ public void close () זורק NetworkException {if (socket.isClosed ()) {לזרוק חדש IllegalStateException ("החיבור כבר סגור."); } נסה {byte message = "נתק".getBytes (); מסונכרן (writeLock) {out.writeInt (Identifier. INTERNAL); out.writeInt (message.length); out.write (הודעה); out.flush (); }} catch (IOException e) {System.out.println ("לא ניתן היה לשלוח הודעת ניתוק."); } נסה {מסונכרן (writeLock) {out.close (); }} catch (IOException e) {throw new NetworkException ("שגיאה בעת סגירת החיבור.", ה); } סוף סוף {thread.interrupt (); }} /*** מחזיר אם החיבור לצד השני חי או לא. * * @return נכון אם החיבור חי. שקר, אחרת. */ boolean public isConnected () {return! socket.isClosed (); }}
מזהה.ג'אווה
/** * המחלקה {@code Identifier} מכילה קבועים המשמשים את {@link Connection} לסידור והסרת הנתונים של הנתונים שנשלחו * ברשת. * * @גרסה 1.0 * @ראה חיבור * / מזהה מחלקה אחרונה ציבורית { / ** * מזהה להודעות פנימיות. */ public static final int INTERNAL = 1; /*** מזהה הודעות טקסט. */ public static final int TEXT = 2; }
NetworkException.java
/*** הכיתה {@code NetworkException} מציינת שגיאה הקשורה לרשת. * / בכיתה ציבורית NetworkException מרחיב את החריגה { / *** בונה {@code NetworkException} עם {@code null} כמסר שלו. * / public NetworkException () {} / *** בונה {@code NetworkException} עם ההודעה שצוין. * * הודעת @param הודעה לתיאור השגיאה. */ public NetworkException (הודעת מחרוזת) {super (הודעה); } /*** בונה {@code NetworkException} עם ההודעה והסיבה שצוינו. * * הודעת @param הודעה לתיאור השגיאה. * @param סיבה סיבה לשגיאה. */ public NetworkException (הודעת מחרוזת, סיבה שניתן לזרוק) {super (הודעה, סיבה); } /*** בונה {@code NetworkException} עם הגורם שצוין. * * @param סיבה סיבה לשגיאה. */ public NetworkException (סיבה שניתן לזרוק) {super (סיבה); }}
UsageExample.java
/*** הכיתה {@code UsageExample} מציגה את השימוש ב- {@link Server} וב- {@link Client}. דוגמאות אלה משתמשות ב- * {@link Thread#sleep (long)} כדי להבטיח שכל קטע מבוצע מכיוון שהתחלה וסגירה מהירה גורמת לכך שחלק מה * קטעים לא יופעלו. * * @version 1.0 * @see Server * @see Client */ class public UsageExample {public static void main (String args) זורק חריגה {String host = "localhost"; int port = 10430; שרת שרת = שרת חדש (מארח, יציאה); לקוח לקוח = לקוח חדש (מארח, יציאה); Thread.sleep (100 ליטר); client.send ("שלום."); server.broadcast ("היי, חבר!"); Thread.sleep (100 ליטר); server.disconnect (server.getConnections () [0]); // או client.close () כדי להתנתק מהשרת בצד client.close (); }}