القائمة الرئيسية

الصفحات

مفهوم ال serialization وال Generics والشبكات في جافا

مفهوم الـ serialization في جافا   

في معظم البرامج التي نستخدمها, نلاحظ أنه يوجد مكان خاص لضبط إعدادات البرنامج يسمح للمستخدم أن يخصص البرنامج بالشكل الذي يريده.
بعد أن يقوم المستخدم بضبط إعدادت برنامجه, نلاحظ أن هذه الإعدادات تظل محفوظة في البرنامج حتى إذا خرج من البرنامج و فتحه من جديد.

فمثلاً في البرامج التي تستخدم للكتابة, تجد في صفحة الإعدادات أنه بإمكانك تغير أنواع الخطوط المستخدمة في البرنامج, و تغيير أحجامها و ألوانها. و وقد تجد في الإعدادات أيضاً أنه بإمكانك إستخدام التطبيق بأكثر من لغة إلخ..
بالنسبة للألعاب, غالباً ما تجد أنه في صفحة الإعدادات يمكنك تحديد ما إذا كنت تريد سماع نغمات حماسية أثناء اللعب أم لا, بالإضافة إلى إمكانية تحديد مستوى الصوت. أيضاً أحياناً تجد أنه يمكنك تحديد مستوى اللعب ( سهل - متوسط - صعب - صعب جداً ).

للحفاظ على إعدادات البرنامج التي ضبطها المستخدم, نقوم بحفظ هذه الإعدادات بداخل ملف و هذا ما ستتعلمه في هذا الدرس.

ملاحظة: عليك فهم درس التعامل مع الملفات جيداً حتى تستطيع فهم هذا الدرس, لأننا سنقوم بتخزن المعلومات في ملف.


شكل الكائن في الذاكرة

أثناء تشغيل البرنامج, كل كائن يتم إنشاءه فيه, يتم تمثيله في الذاكرة كسلسلة كبيرة من الـ Bytes يفهمها نظام التشغيل.
هذه السلسلة تحتوي على جميع معلومات الكائن, مثل:

  • الكلاس المشتق منه.

  • كل كلاس يرث منه.

  • كل إنترفيس يطبقه.

  • كل دالة يملكها.

  • كل متغير يملكه, بالإضافة إلى نوعه و قيمته الحالية إلخ..

عند تنفيذ جميع الأوامر الموجودة في البرنامج أو عند الخروج منه, يتم مسح جميع البيانات المتعلقة بهذا البرنامج من الذاكرة لأنه لم يعد لها حاجة. أي يتم مسح جميع الكائنات, المتغيرات و الدوال, و بالتالي يتوقف أي إتصال قائم بين البرنامج و الأشياء خارجية مثل ملف, شبكة أو قاعدة بيانات إلخ..


مفهوم الـ Serialization و الـ Deserialization

Serialization تعني حفظ حالة الكائن الحالية بداخل ملف.
عندما نقول: "حفظ حالة الكائن", فنحن بذلك نقصد إنشاء نسخة مطابقة من الكائن الموجود في الذاكرة و وضعها في ملف خارجي.

Deserialization تعني استرجاع حالة الكائن الموجودة في ملف.
عندما نقول: "استرجاع حالة الكائن", فنحن بذلك نقصد خلق الكائن الموجود في ملف خارجي بداخل ذاكرة الجهاز.

ملاحظة: بشكل عام عندما نحفظ حالة الكائن و نسترجعها, نقول أننا نفعل Serialization و لكننا فعلياً نفعل Serialization + Deserialization.


أهمية الـ Serialization

  • حفظ حالة الكائن الذي تم إنشاءه في الذاكرة في ملف خارجي.

  • حالة الكائن المحفوظة في ملف يمكن إستخدامها متى شئنا لخلق الكائن من جديد في الذاكرة.

  • مشاركة حالة الكائن عبر شبكة, حيث أنه يمكن استخدام الملف الذي حفظنا فيه حالة الكائن لخلق الكائن في جهاز آخر.

  • تخزين الصور في قواعد البيانات ( الصورة تحفظ في قاعدة البيانات كـ BLOB ).

إذاً في حال أردت حفظ معلومات الكائن قبل الخروج من البرنامج يمكنك إنشاء ملف متزامن يحفظ لك حالة الكائن, و بعدها يمكنك استرجاعها عند تشغيل البرنامج من جديد.


تطبيق الـ Serialization و الـ Deserialization

لتحقيق الـ Serialization, نستخدم الكلاس ObjectOutputStream لإنشاء نسخة من الكائن الموجود في الذاكرة و وضعها في ملف.
لتحقيق الـ Deserialization, نستخدم الكلاس ObjectInputStream لخلق الكائن المحفوظ في الملف في الذاكرة من جديد.

كل كلاس منهم يملك عدة كونستركتورات و دوال, سنشرح فقط الأشياء التي سنستخدمها في هذا الدرس.

خطوات الـ Serialization

لإنشاء كائن من كلاس معين و حفظ حالته عليك اتباع الخطوات التالية:

  1. الكائن الذي تريد حفظ حالته, يجب أن يكون في الأساس مشتق من كلاس يفعل implements للإنترفيس Serializable.

  2. إنشاء ملف إمتداده .ser بواسطة الكلاس FileOutputStream.

  3. تجهيز كائن من الكلاس ObjectOutputStream الذي يستخدم لكتابة حالة الكائن في الملف.

  4. نسخ حالة الكائن الموجود في الذاكرة في هذا الملف بواسطة الدالة writeObject().

  5. عند الإنتهاء من عملية النسخ, نقوم بقطع كل إتصال قمنا بإجرائه مع هذا الملف.


الكلمة المحجوزة transient

في حال أردت عدم نسخ جميع الأشياء المتعلقة بالكائن في الذاكرة, عليك وضع الكلمة transient في تعريف كل شيء لا تريده أن ينسخ في الملف, و عندها سيتم تجاهله.

خطوات الـ Deserialization

لإسترجاع حالة الكائن التي تم حفظها في ملف معين, عليك اتباع الخطوات التالية:

  1. إنشاء كائن فارغ من نفس نوع الكائن الذي نريد إستراجع حالته من الملف.

  2. تجهيز كائن من الكلاس FileInputStream الذي يستخدم لإدخال بيانات ملف محدد في الذاكرة.

  3. تجهيز كائن من الكلاس ObjectInputStream ليعيد خلق الكائن في الذاكرة.

  4. قراءة حالة الكائن بواسطة الدالة readObject() و تخزينها في الكائن الفارغ الذي قمنا بإنشائه في الخطوة الأولى, و هنا سيكون عليك أن تفعل Downcasting لتحول نوع الكائن الذي ترجعه الدالة readObject() إلى نوع الكائن الحقيقي لأنها ترجع الكائن الموجود في الذاكرة كـ Object و ليس كنوعه الحقيقي.

  5. عند الإنتهاء من عملية النسخ, نقوم بقطع كل إتصال قمنا بإجرائه مع هذا الملف.

مثال شامل

في المثال التالي قمنا بتعريف كلاس إسمه Editor, يطبق الإنترفيس Serializable, و يملك المتغيرات التالية:
language, encoding, fontSize, fontFamily, autoSave, autoComplete, direction.

المتغير direction قمنا بتعريفه كـ transient لأننا لا نريد أن يتم حفظ قيمته عندما نفعل Serialization.

بعدها قمنا بتعريف كلاس آخر إسمه Main قمنا فيه بتطبيق مبدأي الـ Serialization و الـ Deserialization.
من السطر 22 إلى السطر 52 قمنا بتطبيق مبدأ الـ Deserialization.
من السطر 59 إلى السطر 90 قمنا بتطبيق مبدأ الـ Serialization.

الملف الذي قمنا بتخزين حالة الكائن فيه قمنا بتسميته user-prefrences.ser.
عند تشغيل البرنامج سيتم إنشاؤه في المجلد الذي يحتوي على المشروع.


إنتبه: في حال ظهرت لك مشكلة في الكلاس Editor قم فقط بإضافة الكود التالي في السطر رقم 6 و سنشرح لك معنى هذا السطر لاحقاً.
private static final long serialVersionUID = 1L;.


مثال

Editor.java
import java.io.Serializable;                      // Serializable هنا قمنا باستدعاء الإنترفيس

	  public class Editor implements Serializable {     // Serializable يطبق الإنترفيس Editor هنا قمنا بتعريف كلاس إسمه

	  public String language;
	  public String encoding;
	  public String fontSize;
	  public String fontFamily;
	  public boolean autoSave;
	  public boolean autoComplete;
	  public transient String direction;            // transient كـ direction قمنا بتعريف المتغير

	  }
	

Main.java
import java.io.File;                     // File هنا قمنا باستدعاء الكلاس
	  import java.io.FileInputStream;          // FileInputStream هنا قمنا باستدعاء الكلاس
	  import java.io.FileOutputStream;         // FileOutputStream هنا قمنا باستدعاء الكلاس
	  import java.io.ObjectInputStream;        // ObjectInputStream هنا قمنا باستدعاء الكلاس
	  import java.io.ObjectOutputStream;       // ObjectOutputStream هنا قمنا باستدعاء الكلاس
	  import java.io.IOException;              // IOException هنا قمنا باستدعاء الكلاس

	  public class Main {

	  public static void main(String[] args) {

	  // e إسمه Editor في كل مرة نقوم فيها بتشغيل البرنامج سيتم إنشاء كائن من الكلاس
	  Editor e = new Editor();


	  // لمعرفة إذا كان يوجد ملف يحفظ حالة الكائن أم لا user-prefrences.ser بعدها سيتم البحث عن الملف
	  if ( new File("./user-prefrences.ser").exists() )
	  {
	  // منه e موجوداً سيحاول البرنامج إستعادة حالة الكائن user-prefrences.ser في حال كان الملف
	  try
	  {
	  // في الذاكرة user-prefrences.ser حتى نستطيع إدخال المعلومات الموجودة في الملف FileInputStream هنا قمنا بإنشاء كائن نوعه
	  FileInputStream fis = new FileInputStream("./user-prefrences.ser");

	  // في الذاكرة user-prefrences.ser المحفوظ في الملف Editor لنتمكن من إعادة خلق كائن الـ ObjectInputStream هنا قمنا بإنشاء كائن نوعه
	  ObjectInputStream ois = new ObjectInputStream(fis);

	  // e و قمنا بتخزين حالته في الكائن Editor هنا قمنا بقراءة حالة الكائن الذي تم خلقه في الذاكرة ككائن من الكلاس
	  e = (Editor) ois.readObject();

	  // user-prefrences.ser في الأخير قمنا بقطع كل إتصال قمنا بإجرائه مع الملف
	  fis.close();
	  ois.close();

	  // في حال عدم حدوث أي خطأ, سيتم طباعة الجملة التالية التي تعني أن العملية تمت بنجاح
	  System.out.println("Deserialized data has been created in the memory");
	  System.out.println("Language:      " + e.language);
	  System.out.println("Encoding:      " + e.encoding);
	  System.out.println("Font size:     " + e.fontSize);
	  System.out.println("Font family:   " + e.fontFamily);
	  System.out.println("Auto save:     " + e.autoSave);
	  System.out.println("Direction:     " + e.direction);
	  System.out.println("Auto Complete: " + e.autoComplete);
	  System.out.println();
	  }
	  catch(IOException | ClassNotFoundException ex)
	  {
	  // في حال حدوث أي خطأ عند محاولة إسترجاع حالة الكائن سيتم عرضعه
	  System.out.println(ex.getMessage());
	  }
	  }




	  // user-prefrences.ser و حفظها في ملف جديد إسمه e هنا حاولنا تغيير حالة الكائن
	  try
	  {
	  // ( أي قمنا بتغيير إعدادات البرنامج ) e هنا قمنا بتغيير قيم الكائن
	  e.language   = "arabic";
	  e.encoding   = "utf-8";
	  e.fontSize   = "12pt";
	  e.fontFamily = "tahoma";
	  e.autoSave   = true;
	  e.direction  = "right to left";

	  // .ser إمتداده ,user-prefrences.ser هنا قمنا بإنشاء ملف إسمه
	  FileOutputStream fos = new FileOutputStream("./user-prefrences.ser");

	  // user-prefrences.ser لنتمكن من استخراج حالة أي كائن موجود في الذاكرة و وضعها في الملف ObjectOutputStream هنا قمنا بإنشاء كائن نوعه
	  ObjectOutputStream oos = new ObjectOutputStream(fos);

	  // لحفظ الإعدادات التي قمنا بإدخالها user-prefrences.ser في الملف e هنا قمنا بنسخ حالة الكائن
	  oos.writeObject(e);

	  // user-prefrences.ser في الأخير قمنا بقطع كل إتصال قمنا بإجرائه مع الملف
	  oos.close();
	  fos.flush();
	  fos.close();

	  // في حال عدم حدوث أي خطأ, سيتم طباعة الجملة التالية التي تعني أن العملية تمت بنجاح
	  System.out.println("Serialized data has been saved in the project in a file called user-prefrences.ser");
	  }
	  catch(IOException ex)
	  {
	  // في حال حدوث أي خطأ عند نسخ البيانات من الذاكرة إلى الملف سيتم عرضه
	  System.out.println(ex.getMessage());
	  }

	  }

	  }
	

في المرة الأولى التي تقوم فيها بتشغيل البرنامج ستحصل على النتيجة التالية.

Serialized data has been saved in the project in a file called user-prefrences.ser 
	

في المرة الثانية التي تقوم فيها بتشغيل البرنامج ستحصل على النتيجة التالية.

Deserialized data has been created in the memory
	  Language:      arabic
	  Encoding:      utf-8
	  Font size:     12pt
	  Font family:   tahoma
	  Auto save:     true
	  Direction:     null
	  Auto Complete: false

	  Serialized data has been saved in the project in a file called user-prefrences.ser 
	

بما أنه قد تم إنشاء الملف user-prefrences.ser بنجاح, يمكنك البحث عنه و فتحه بواسطة أي محرر و عندها ستتمكن من رؤية شكل المعلومات التي كانت مسجلة في الذاكرة.

لاحظ أنه لم يتم حفظ قيمة المتغير direction في الملف لأننا قمنا بتعريفها كـ transient, لذلك تم إعطائه القيمة null كقيمة إفتراضية.

المتغير serialVersionUID

كل كلاس يطبق الإنترفيس Serializable يتم إعطاءه رقم إصدار خاص فيه.
هذا الرقم يتم تخزينه في المتغير serialVersionUID.

إذاً كل كلاس يطبق الإنترفيس Serializable, يملك متغير إسمه serialVersionUID حتى لو لم يتم تعريفه.

رقم الإصدار يضمن أن المرسل و المستقبل للملف على الشبكة يملكون نفس نسخة الكلاس للكائن المحفوظ في الملف.
في حال كان رقم الإصدار في كلاس المرسل مختلف عن رقم الإصدار في كلاس المستقبل يتم رمي إستثناء من النوع InvalidClassException.

إذاً رقم الإصدار serialVersionUID مهم جداً عند بناء تطبيق يشارك البيانات بين سيرفر و عميل, أي يوجد تطبيق على السيرفر و تطبيق عند المستخدم العادي مرتبطان مع بعضهما البعض. سنرى ذلك في الدرس التالي.

بشكل عام, تعريف المتغير serialVersionUID ليس أمراً إجبارياً في حال كنت تبني برنامج لا تشارك فيه البيانات مع برنامج آخر, لأن جافا أصلاً ستقوم بتعريفه عنك في حال لم تقم بتعريفه بنفسك, لكن في بعض بيئات العمل مثل بيئة Eclipse, نلاحظ أن الـ Complier يظهر تحذير في حال لم نقم بتعريف المتغير serialVersionUID من جديد في البرنامج, لذلك ننصحك بتعريفه في جميع الحالات لأنه لن يؤثر أصلاً على الكود.


طريقة تعريف المتغير serialVersionUID

في البداية يمكنك وضع أي Access Modifier و لن يشكل ذلك أي فرق هنا, لكنك مجبر على تعريفه كـ static final long.


مثال

في الإصدار الأول من الكلاس Editor وضعنا قيمته 1L

private static final long serialVersionUID = 1L; 
	

في الإصدار الثاني من الكلاس Editor وضعنا قيمته 2L

private static final long serialVersionUID = 2L; 
	

مقدمة للـ Generics في جافا

في الدرسين السابقين تعرفنا على كلاسات الـ Data Structure التي تسمح لنا بتخزين البيانات مهما كان نوعهم و بأشكال مختلفة أثناء تشغيل البرنامج.
عند استخدام هذه الكلاسات كنا نلاحظ أنه بإمكاننا تخزين أي نوع بيانات فيهم بدون أي مشاكل.

أسلوب الـ Generics يجعلك قادراً على بناء كود واحد يلائم أكثر من نوع بيانات, و عند الحاجة إلى استخدام هذا الكود يمكنك إستخدامه كما هو, و يمكنك أيضاً تحديد نوع البيانات التي تريده أن يعمل معها حسب حاجتك.

في الدرس السابق شرحنا كيف يمكن تخزين أي نوع بيانات في الكلاسات ArrayList و LinkedList, و تعلمنا أيضاً كيف أننا نستطيع تحديد نوع البيانات التي نريد تخزينها فيهم في حال استخدمنا أسلوب الـ Generics عند إنشاء كائنات منهم.


معلومة تقنية

فعلياً, معظم كلاسات الـ Data Structure تم بناءها على أساس هذا الأسلوب.
كل كلاس, إنترفيس أو دالة تجد الرمز < > موجود ضمن تعريفه, إعرف مباشرةً أنه مصمم للتعامل مع أكثر من نوع بيانات.


فائدة الـ Generics

أنت لست مجبر على استخدام أسلوب الـ Generics, لكن تعلمه سيفيدك كثيراً في تطوير الكود الخاص فيك, و جعل حجمه أصغر و أسهل في حالة التعديل.
فمثلاً, إن كنت بحاجة إلى بناء مصفوفة ليس لها حجم محدد و تريد تخزين أعداد صحيحة ( أي من النوع int ) فقط فيها, يمكنك إنشاء كائن من الكلاس ArrayList و تحديد النوع Integer كنوع البيانات الوحيد الذي يمكن إدخاله في هذا الكائن كما رأينا في الدرس السابق و تكون بهذا قد وصلت لهدفك بكل سهولة بواسطة أسلوب الـ Generics.

ميزة أخرى مهمة جدًا في هذا الأسلوب, و هي أنه يساعد المبرمج في إكتشاف الأخطاء النوعية أثناء كتابة الكود.

مثلاً, إذا قمت بإنشاء كائن نوعه ArrayList و حددت أنه يمكنه تخزين قيم من النوع Integer فقط, بعدها حاولت تخزين قيمة من نوع آخر مثل النوع String على سبيل المثال, عندها سيظهر لك تحذير مفاده أنه لا يمكن تحويل النوع String إلى النوع Integer.


خلاصة

بشكل عام, نستخدم الـ Generics لبناء كود يتوافق مع أي نوع بيانات.
عند الحاجة إلى هذا الكود نقوم بتحديد نوع البيانات التي سنتعامل معها. ستفهم كل شيء من الأمثلة.


ستتعلم في هذا الدرس كيف تعرف أي شيء كـ Generic و كيف تستفيد منه, و تذكر أنه يمكنك استخدام هذا الأسلوب مع أغلب كلاسات الـ Data Structure.

الأحرف المستخدمة في الـ Generics في جافا

ذكرنا في الجدول التالي الأحرف المتعارف عليها بين المبرمجين عند التعامل مع الـ Generics.

الأحرف المتعارف عليها في الـ Generics
T إختصار للكلمة Type, يقصد منه أي نوع كان.
نستخدمه في العادة عند بناء كود يتعامل مع أي نوع بيانات.
N إختصار للكلمة Number, يقصد منه أي نوع من الأنواع التي تستخدم لتخزين الأرقام.
نستخدمه في العادة عند بناء كود يتعامل مع الأرقام من أي نوع كانت مثل ( Integer - Float - Double إلخ.. ).
E إختصار للكلمة Element, يقصد منه عنصر ليس له نوع محدد.
نستخدمه في العادة عند بناء كود يتعامل مع عناصر المصفوفة من أي نوع كانت.
K إختصار للكلمة Key, يقصد منه مفتاح ليس له نوع محدد.
نستخدمه في العادة عند بناء كود يتعامل مع مفاتيح كائن يخزن البيانات بشكل key / value.
V إختصار للكلمة Value, يقصد منه قيمة ليس لها نوع محدد.
نستخدمه في العادة عند بناء كود يتعامل مع قيم كائن يخزن البيانات بشكل key / value.

ملاحظة: جميع الأحرف المذكورة في الجدول ليس لها أي تأثير على الكود و يمكنك وضع أي حرف أو كلمة مكانهم, لكننا ننصحك باعتمادهم.

المبادئ الأساسية التي عليك إتباعها عند التعامل مع الـ Generics في جافا

لتعريف متغير, مصفوفة أو دالة بدون تحديد نوعهم, أي ليعملوا مع مختلف أنواع البيانات, يجب تعريفهم كـ Generics.
هنا وضعنا أمثلة صغيرة الهدف منها توضيح طريقة استخدام الأحرف التي ذكرناها في الجدول السابق من أجل تعريف كود يمكن استخدامه للتعامل مع أكثر من نوع بيانات.

في نهاية الدرس وضعنا أمثلة مهمة توضح أهمية و طريقة استخدام الـ Generics.


طريقة تعريف نوع بيانات مجهول

لتجهيز نوع بيانات غير محدد يمكن استخدامه في كلاس أو إنترفيس أو دالة, نضع أحد الأحرف التي ذكرناها في الجدول السابق بين &lt > عند تعريفهم.

أمثلة

هنا قمنا بتعريف كلاس إسمه GenericClass يملك نوع بيانات غير محدد رمزنا له بـ T.

                    public class GenericClass <T> {

	  }
	

هنا قمنا بتعريف إنترفيس إسمه GenericInterface يملك نوع بيانات غير محدد رمزنا له بحرف N.

                    public interface GenericInterface <N> {

	  }
	

هنا قمنا بتعريف دالة إسمها genericMethod تملك نوع بيانات غير محدد رمزنا له بحرف E.

                    public <E> void genericMethod(E var) {
	  System.out.println(var);
	  } 
	


طريقة تعريف أكثر من نوع بيانات مجهول

نفس الطريقة السابقة, لكن يجب وضع فاصلة بين كل حرفين تم استخدامها بداخل الرمز &lt >

أمثلة

هنا قمنا بتعريف كلاس إسمه GenericClass يملك نوعي بيانات غير محددين يرمز لهما بالحرفين E1 و E2.

                    public class GenericClass <E1, E2> {

	  }
	

هنا قمنا بتعريف إنترفيس إسمه GenericInterface يملك نوعي بيانات غير محددين يرمز لهما بالحرفين E1 و E2.

                    public interface GenericInterface <E1, E2> {

	  }
	

هنا قمنا بتعريف دالة إسمها genericMethod تملك نوعي بيانات غير محددين يرمز لهما بالحرفين T1 و T2.

                    public <T1, T2> void genericMethod(T1 a, T2 b) {
	  System.out.println(a);
	  System.out.println(b);
	  } 
	

أمثلة شاملة حول التعامل مع الـ Generics في جافا

التعامل مع الـ Generics يكون بأشكال مختلفة ستتعرف عليها من الأمثلة التي وضعناها لك, كما أننا وضعنا خلاصة في نهاية كل مثال.


مصطلحات تقينة

  • Generic Method: تعني تعريف دالة تتعامل مع أكثر من نوع بيانات.

  • Generic Class: تعني تعريف كلاس يتعامل مع أكثر من نوع بيانات.

  • Bounded Type Parameters: تعني تعريف كلاس, إنترفيس أو دالة يتعاملوا مع نوع محدد. بالإضافة إلى جميع الأنواع المشتقة منه, أي التي ترث منه.



المثال الأول

 هذا المثال  هو تطبيق لفكرة الـ Generic Method.
ستتعلم منه كيف تستغل مبدأ الـ Generics لبناء دالة تتعامل مع مختلف أنواع المصفوفات.

شاهد المثال »



المثال الثاني

هذا المثال  هو تطبيق لفكرة الـ Generic Class.
ستتعلم منه كيف تستغل مبدأ الـ Generics لبناء كلاس يتعامل مع مختلف الأنواع.

شاهد المثال »



المثال الثالث

هذا المثال  هو تطبيق لفكرة الـ Bounded Type Parameters.
ستتعلم في منه كيف تستغل مبدأ الـ Generics لبناء دوال تتعامل مع مختلف أنواع الأرقام فقط.

شاهد المثال »

 مثال حول تعريف و إستدعاء Generic Method في جافا

في المثال التالي قمنا ببناء دالة إسمها printArray, نوعها public static void.
هذه الدالة مصممة بشكل عام للتعامل مع مصفوفة ليس لها نوع محدد و هذا سبب وضع الحرف <E> بين الكلمتين static و void.

عند استدعاء هذه الدالة نمرر لها مصفوفة من أي نوع فتقوم بعرض محتواها.
إذاً E[] تعني أنه عند استدعاء هذه الدالة يجب تمرير مصفوفة من أي نوع كان فيها كـ Argument مكان الباراميتر array.

في كل مرة تقوم فيها باستدعاء الدالة printArray() يتم تبديل الحرف E بنوع المصفوفة التي تم إدخالها فيها مكان الباراميتر array.


Main.java
                    public class Main {

	  // argument عند استدعاءها نمرر لها مصفوفة من أي نوع كـ .void هنا قمنا بتعريف دالة نوعها
	  public static <E> void printArray( E[] array )
	  {
	  // التي ندخلها في الدالة عند استدعاءها array تعرض جميع عناصر المصفوفة foreach هنا قمنا بإنشاء حلقة
	  for( E element : array )
	  System.out.print(element + " ");

	  System.out.println();

	  }


	  public static void main(String[] args) {

	  // هنا قمنا بتعريف 6 مصفوفات, كل واحدة فيهم تملك 5 عناصر من نوع مختلف
	  Integer   arr1[] = {1, 2, 3, 4, 5};
	  Long      arr2[] = {1l, 2l, 3l, 4l, 5l};
	  Float     arr3[] = {1f, 2f, 3f, 4f, 5f};
	  Double    arr4[] = {1d, 2d, 3d, 4d, 5d};
	  Character arr5[] = {'a', 'b', 'c', 'd', 'e'};
	  String    arr6[] = {"Generics ", "are ", "easy ", "to ", "understand"};

	  // لعرض محتوى جميع المصفوفات printArray() هنا قمنا باستدعاء الدالة
	  printArray( arr1 );
	  printArray( arr2 );
	  printArray( arr3 );
	  printArray( arr4 );
	  printArray( arr5 );
	  printArray( arr6 );

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    1 2 3 4 5
	  1 2 3 4 5
	  1.0 2.0 3.0 4.0 5.0
	  1.0 2.0 3.0 4.0 5.0
	  a b c d e
	  Generics are easy to understand 
	

 مثال حول تعريف Generic Class و إنشاء كائن منه في جافا

في المثال التالي قمنا بتعريف كلاس إسمه Box, يملك نوع بيانات مجهول رمزنا له بالحرف T.
في هذا الكلاس قمنا بتعريف متغير إسمه x نوعه T.

ثم قمنا ببناء دوال للتعامل مع المتغير x:

  • الدالة getX()تستخدم لجلب قيمة المتغير x.

  • الدالة setX()تستخدم لتحديد قيمة المتغير x.

في الأخير قمنا بإنشاء الكلاس Main لتجربة إنشاء كائنات من الكلاس Box.


إنتبه

عند إنشاء كائن من الكلاس Box في حال لم تقم بوضع كلاس مكان الحرف <T> سيتم إعتبار x من النوع Object.
أما في حال قمت بوضع كلاس مكان الحرف <T>, سيتم تبديل جميع الأحرف T الموجودة في الكلاس Box بإسم الكلاس الذي أدخلته مكانها.


Box.java
                    public class Box <T> {

	  private T x;

	  public void set(T x) {
	  this.x = x;
	  }

	  public T get() {
	  return x;
	  }

	  }
	

Main.java
                    public class Main {

	  public static void main(String[] args) {

	  // يمكنه تخزين أي نوع بيانات objectBox إسمه Box هنا قمنا بتعريف كائن من الكلاس
	  Box objectBox = new Box();

	  // Object ملاحظة سيتم تخزين النص كـ .x سيتم تخزين هذه القيمة في المتغير .String هنا أدخلنا فيه قيمة نوعها
	  objectBox.set( "object type can store any type of data" );

	  // أيضاً Object القيمة التي سترجع هنا يكون نوعها .x هنا قمنا بإرجاع القيمة التي تم تخزينها في المتغير
	  System.out.println("objectBox contains: " + objectBox.get());

	  // أيضاً Object مكان القيمة السابقة, و سيتم تخزينها كـ x سيتم تخزين هذه القيمة في المتغير .Integer هنا أدخلنا فيه قيمة نوعها
	  objectBox.set( 1234 );

	  // أيضاً Object القيمة التي سترجع هنا يكون نوعها .x هنا قمنا بإرجاع القيمة التي تم تخزينها في المتغير
	  System.out.println("objectBox contains: " + objectBox.get());



	  // Integer يمكنه فقط تخزين قيم نوعها integerBox إسمه Box هنا قمنا بتعريف كائن من الكلاس
	  Box<Integer> integerBox = new Box();

	  // أيضاً Integer و الذي أصبح نوعه x سيتم تخزين هذه القيمة في المتغير .Integer هنا قمنا بإدخال قيمة فيه نوعها
	  integerBox.set( 100 );

	  // أيضاً Integer القيمة التي سترجع هنا يكون نوعها .x هنا قمنا بإرجاع القيمة التي تم تخزينها في المتغير
	  System.out.println("integerBox contains: " + integerBox.get());



	  // String يمكنه فقط تخزين قيم نوعها stringBox إسمه Box هنا قمنا بتعريف كائن من الكلاس
	  Box<String> stringBox = new Box();

	  // أيضاً String و الذي أصبح نوعه x سيتم تخزين هذه القيمة في المتغير .String هنا قمنا بإدخال قيمة فيه نوعها
	  stringBox.set( "Hello, my type is String" );

	  // أيضاً String القيمة التي سترجع هنا يكون نوعها .x هنا قمنا بإرجاع القيمة التي تم تخزينها في المتغير
	  System.out.println("stringBox contains: " + stringBox.get());

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    objectBox contains: object type can store any type of data
	  objectBox contains: 1234
	  integerBox contains: 100
	  stringBox contains: Hello, my type is String
	

إذاً نقوم بتعريف الكلاس كـ Generic عندما يكون عندنا عمليات في هذا الكلاس تطبق على أكثر من نوع.
فبدل إنشاء كلاس للتعامل مع كل نوع على حدة, نعرف الكلاس كـ Generic , و نحدد النوع الذي نريد التعامل معه أثناء إنشاء كائن من هذا الكلاس.

 مثال حول Bounded Type Parameters في جافا

في المثال التالي قمنا بتعريف دالتين إسمهم maximum, أي فعلنا Overload هنا.

الدالة الأولى نعطيها رقمين من أي نوع كان سواء (int - long - float - double ) فتقوم بإرجاع العدد الأكبر بينهما.
الدالة الثانية نعطيها ثلاث أرقام من أي نوع كان أيضاً فتقوم بإرجاع العدد الأكبر بينهما.

ملاحظة: جعلنا الدالة الثانية تستدعي الدالة الأولى مرتين بدل كتابتها من الصفر.


Main.java
                    public class Main {

	  // هنا قمنا ببناء دالة نعطيها رقمين من أي نوع, فتقوم بمقارنتهما و ترجع الرقم الأكبر
	  public static <N extends Number> N max (N x, N y)
	  {
	  // حتى نستطيع مقارنتهم, بعدها سيتم إرجاع العدد الأكبر بينهما double هنا سيتم تحويل نوع الأرقام المدخلة إلى النوع
	  if ( x.doubleValue() > y.doubleValue() )
	  return x;
	  else
	  return y;
	  }


	  // هنا قمنا ببناء دالة مشابهة للدالة السابقة, لكنها تقارن ثلاث أرقام مع بعض
	  public static <N extends Number> N max (N x, N y, N z)
	  {
	  // بكل بساطة, تستدعي الدالة السابقة لمقارنة أول عددين, و معرفة الأكبر بينهما
	  // بعدها تستدعيها من جديد لمقارنة العدد الأكبر بين العددين السابقين مع العدد الأخير
	  return max( max(x, y), z );
	  }


	  public static void main(String[] args) {

	  // التي تأخذ باراميترين max() هنا قمنا بإجراء ثلاث عمليات مقارنة باستخدام الدالة

	  // int هنا قمنا بمقارنة رقمين نوعهما
	  System.out.println( max(3, 7) );

	  // long مع رقم نوعه float هنا قمنا بمقارنة رقم نوعه
	  System.out.println( max(5.5F, 8L) );

	  // long مع رقم نوعه float هنا قمنا بمقارنة رقم نوعه
	  System.out.println( max(6.9D, 8L) );

	  // التي تأخذ ثلاث باراميترات max() هنا قمنا بإجراء عمليتين مقارنة تعتمدان على الدالة

	  // int هنا قمنا بمقارنة ثلاث أرقام نوعهم
	  System.out.println( max(10, 15, 5) );

	  // double مع رقم نوعه float مع رقم نوعه int هنا قمنا رقم نوعه
	  System.out.println( max(11, 12.5F, 17.8D) );

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    7
	  8
	  8
	  15
	  17.8
	

إذاً عندما نقول أن النوع المجهول يرث من كلاس محدد. فهذا يعني أن الكود مصمم للتعامل مع هذا الكلاس, بالإضافة إلى جميع الكلاسات التي ترث منه.

 التعامل مع الشبكات في جافا | Java Networking

شبكة تعني Network في اللغة الإنجليزية, الفكرة الأساسية من الشبكات هي ربط الأجهزة على إختلاف أنواعها مع بعض لخلق نوع من التواصل فيما بينهم. فمثلاً الأجهزة الموصولة على الشبكة سواء كانت كمبيوترات أو هواتف أو سيرفرات, يمكنها مشاركة البيانات مع بعضها البعض بواسطة برامج معينة.

Network Programming تعني كتابة برنامج يشارك بياناته مع برنامج آخر من خلال شبكة.
في العادة يكون برنامج مثبت على السيرفر و برنامج مثبت عند المستخدم العادي.
الفكرة هنا بناء برامج للمستخدمين تجعلهم قادرين على التواصل مع بعضهم و مشاركة البيانات فيما بينهم.

عندما نقول مشاركة البيانات من خلال شبكة, نقصد بذلك أن الكمبيوتر سيشارك بياناته مع كمبيوتر آخر.
يمكن أن يكون كلا الكمبيوترات موجودين على نفس الشبكة و يمكن أن يكون كل كمبيوتر منهم في بلد مختلف ( لا يهم ).

لبناء برامج يمكنها مشاركة البيانات مع بعضها عبر الشبكات, نستخدم الحزمة java.net التي تحتوي على مجموعة ضخمة من الكلاسات و الإنترفيسات المصممة خصيصياً لمشاركة البيانات عبر الشبكات.


فائدة بناء برامج تتواصل مع بعضها من خلال شبكة

  1. مشاركة البيانات بين مختلف المستخدمين على الشبكة.

  2. تجميع البيانات التي نريد مشاركتها في مكان واحد, الأمر الذي يجعلنا قادرين على التحكم في طريقة مشاركة هذه البيانات.


أفكار لبرامج تعمل على الشبكة

يوجد أفكار كثيرة لبرامج يستخدمها الناس لمشاركة بياناتهم و التي يمكنك بناءها بنفسك أيضاً, أمثلة:

  • برنامج محادثة, مثل MSN, Yahoo, Telegram, Whatsapp.

  • متصفح, حيث يمكنك بناء متصفح مثل Mozilla FireFox, Google Chrome, Internet Explorer,Opera Mini.

  • ألعاب, حيث أنه يمكنك بناء لعبة يستطيع فيها اللاعبين تحدي بعضهم عن بعد.

  • برنامج إختبار, فمثلاً, يمكنك بناء برنامج يجعل الطالب قادراً على إجراء أي إمتحان عن بعد و إرساله إلى مركز المؤسسة التعليمية الرئيسية عند الإنتهاء إلخ..

مصطلحات أساسية في عالم الشبكات

في الجدول التالي وضعنا بعض المصطلحات الأساسية في عالم الشبكات التي سنتطرق إلى بعضها في هذا الدرس.
ملاحظة: في مادة الشبكات سنشرحهم بتفصيل و دقة أكثر.

المصطلح معناه
NIC NIC إختصار لـ Network Interface Card, و معناه كرت الشبكة.
كرت الشبكة هو قطعة إلكترونية يتم تركيبها في أي جهاز إلكتروني حتى يستطيع الإتصال بالشبكات.
MAC Address MAC Address إختصار لـ Media Access Control Address, و هو عبارة عن رقم كرت الشبكة الخاصة بالجهاز.
كل كرت شبكة يملك MAC Address موحد.
الـ MAC Address يتألف من 12 bytes, كل byte فيهم يمكن أن يكون رقم بين 0 و 9, أو حرف بين A و F.

أمثلة
  • 00:1C:B3:09:85:15

  • 00:B0:D0:86:BB:F7

  • 00:1B:44:11:3A:B7

IP Address IP Address إختصار لـ Internet Protocol Address, و هو عبارة عن عنوان مؤقت يتم إعطائه لأي جهاز يريد الإتصال بالشبكة.
كل جهاز يريد الإتصال بالشبكة يتم إعطائه IP Address مختلف.
الـ IP Address يتألف من أربعة أرقام, كل رقم فيهم يمكن أن يكون بين 0 و 255.

أمثلة
  • 80.10.5.104

  • 192.168.0.1

  • 210.14.92.65

Protocol في العربية أيضاً نقول بروتوكول, و هو عبارة عن مجموعة قواعد أساسية يجب إتباعها في كل عملية تواصل.
في عالم الشبكات يوجد بروتوكولات كثيرة, كل واحد منهم مصمم لغرض معين.

أشهر البروتوكولات
  • TCP: يستخدم عند نقل بيانات على الشبكة, يضمن وصولها إلى الجهة المطلوبة كما هي تماماً.

  • UDP: يستخدم عند نقل بيانات على الشبكة, لا يهمه إذا فقد جزء من البيانات التي تم إرسالها.

  • HTTP: يستخدمه المتصفح عندما تقوم بتصفح المواقع.

  • FTP: يستخدم عند تحميل أي نوع من الملفات.

  • Telnet: يستخدم للتحكم في البرامج عن بعد.

  • POP3: يستخدم من قبل العميل لإستقبال الرسائل إلكترونية الواردة في بريده مع إمكانية حذفها.

  • SMTP: يستخدم من قبل العميل لإرسال رسائل إلكترونية إلى السيرفر.

Port Number في العربية نقول رقم البورت.
كل برنامج يرسل أو يستقبل بيانات على الشبكة يحتاج أن يفتح بورت خاص فيه على الجهاز.
رقم البورت يتيح للمستخدم إستخدام أكثر من برنامج يتعاملون مع الشبكة في نفس الوقت.
إذاً رقم البورت يميز البرامج التي تستخدم الشبكة عن بعضها البعض.
URL إختصار لـ Uniform Resource Locator, و يعني كل المعلومات المذكورة في الرابط و التي تشير إلى مصدر محدد للبيانات.

المعلومات الأساسية في أي رابط:   protocol://prefix.domain:port/path/filename
  1. protocol نضع مكانها إسم البروتوكول المستخدم في عملية الإتصال مثل http

  2. prefix نضع مكانها في العادة www

  3. domain نضع مكانها إسم الموقع مثل harmash.com

  4. port نضع مكانها بورت البرنامج أو البروتوكول المستخدم من قبل العميل, فمثلاً الـ http يستخدم البورت 80

  5. path نضع مكانها إسم المجلد الذي نريد الدخول إليه على السيرفر.

  6. filename نضع مكانها إسم الملف الذي نريد تصفحه أو تحميله إلخ..


ملاحظة: عندما تتصفح أي موقع في النت, حتى لو لم ترا جميع المعلومات التي ذكرناها لك عن الـ URL, تأكد أن المتصفح يكتبها عنك.

أمثلة
Socket الـ Socket عبارة عن مجموعة الكلاسات التي يمكن إستخدامها لإنشاء برامج تجعل المستخدمين قادرين على التواصل فيما بينهم بشكل متزامن عبر شبكة النت. و هي تضمن المعلومات الأساسية التي نحتاجها لإجراء أي عملية إتصال: Socket = IP Address + Port Number + Protocol

مثال
في حال أردنا إرسال بيانات على الشبكة إلى شخص محدد, علينا تحديد المعلومات التالية:
  1. البروتوكول الذي سنعتمده عليه لإجراء هذه العملية.

  2. عنوان الشخص الذي سنرسل له البيانات.

  3. رقم بورت البرنامج الذي سنستخدمه لإرسال البيانات.


خلاصة

يمكن الوصول لأي جهاز على الشبكة من خلال الـ IP Address الخاص فيه.
و كل تطبيق يتعامل مع الشبكات سواء لإرسال أو إستقبال بيانات يحتاج إلى رقم بورت خاص فيه ليميزه عن باقي البرامج التي تستخدم الشبكة.

الكلاسات التي نحتاجها للتعامل مع الشبكات في جافا

الكلاس مع تعريفه
public class URL هذا الكلاس مصمم بشكل عام لتجهيز روابط الملفات التي سنتعامل معها عن بعد عبر شبكة الإنترنت.
تابع القراءة »
public class URLConnection هذا الكلاس عبارة عن Abstract Class, و هو يستخدم للقراءة و الكتابة في الملف الذي يشير له كائن الـ URL, بالإضافة إلى أنه يمكننا من معرفة و تحديد كافة المعلومات المتعلقة بإجراء الإتصال مع الملف.
فمثلاً يتيح لنا معرفة آخر مرة تم فيها تحديث الملف, تاريخ صلاحية الملف, إسم الكلاس المناسب لإرسال المعلومات منه و استقبالها فيه إلخ..
تابع القراءة »
public class InetAddress هذا الكلاس مصمم لتحليل عنوان الجهاز أو الموقع الذي نحاول الإتصال به عن بعد عبر شبكة النت.
تابع القراءة »
public class Socket + public class ServerSocket الـ Socket عبارة عن مجموعة من الكلاسات التي يمكن إستخدامها لإنشاء برامج تجعل المستخدمين قادرين على التواصل فيما بينهم بشكل متزامن عبر شبكة النت, لذلك تعتبر أهم كلاسات موجودة في الحزمة java.net و هي تنقسم لقسمين أساسيين:

  • كلاسات مخصصة لبناء برامج تستخدم البروتوكول UDP, مثل الكلاسات DatagramSocket و DatagramPacket.

  • كلاسات مخصصة لبناء برامج تستخدم البروتوكول TCP, مثل الكلاسات Socket و ServerSocket.

هنا قمنا بشرح الكلاسات Socket و ServerSocket.
تابع القراءة »

 الكلاس URL في جافا

الكلاس URL في جافا

الكلمة URL إختصار لـ Uniform Resource Locator و هي تعني كل المعلومات المذكورة في الرابط و التي تشير إلى مصدر محدد للبيانات.

الكلاس URL مصمم بشكل عام لتجهيز روابط الملفات التي سنتعامل معها عن بعد عبر شبكة الإنترنت.


بناؤه

                  public final class URL
	extends Object
	implements Serializable 
  

المعلومات الأساسية في أي رابط هي: protocol://host:port/path/filename

  • protocol: نضع مكانها إسم البروتوكول المستخدم في عملية الإتصال مثل http أو https.

  • host: نضع مكانها إسم الموقع مثل harmash.com.

  • port: نضع مكانها بورت البرنامج ( أو البروتوكول ) المستخدم من قبل العميل, فمثلاً https يستخدم البورت 443.

  • path: نضع مكانها إسم المجلد الذي نريد الدخول إليه على السيرفر مثل مجلد home.

  • filename: نضع مكانها إسم الملف الذي نريد تصفحه أو تحميله إلخ.. مثل الملف index.php.


إذا قمنا بتركيب جميع المعلومات المتفرقة الذتي ذكرناها, سنحصل على رابط واحد و هو رابط الدرس الأول في دروس جافا.

كونستركتورات الكلاس URL

بشكل عام عندما تنشئ كائن نوعه URL فأنت تنشئ كائن يمثل رابط.

الجدول التالي يحتوي على بعض الكونستركتورات الموجودين في الكلاس URL.

الكونستركتور مع تعريفه
public URL(String url)
throws MalformedURLException
ينشئ كائن نوعه URL من خلال النص الذي نمرره له.
النص الذي نقوم بإدخاله يمثل كافة المعلومات التي تشكل رابط.
في حال وجود أي خطأ في المعلومات المدخلة فيه يتم رمي الإستثناء MalformedURLException.
public URL(String protocol, String host, int port, String file)
throws MalformedURLException
ينشئ كائن نوعه URL مع تحديد البروتوكول + إسم الموقع + رقم البورت + إسم الملف الذي سيتم الوصول إليه من خلاله.
في حال وجود أي خطأ في المعلومات المدخلة فيه يتم رمي الإستثناء MalformedURLException.
public URL(String protocol, String host, String file)
throws MalformedURLException
ينشئ كائن نوعه URL مع تحديد البروتوكول + إسم الموقع + إسم الملف الذي سيتم الوصول إليه من خلاله.
عندما لا تقوم بتحديد رقم البورت بنفسك, يقوم باستخدام رقم البورت الإفتراضي للبروتوكول المذكور.
في حال وجود أي خطأ في المعلومات المدخلة فيه يتم رمي الإستثناء MalformedURLException.

دوال الكلاس URL

الجدول التالي يحتوي على دوال الكلاس URL.

الدالة مع تعريفها
public String getHost() ترجع نص يمثل البروتوكول + إسم الموقع الموضوعين في كائن الـ URL.
public String getAuthority() ترجع نص يمثل البروتوكول + إسم الموقع + رقم البورت (في حال كان مذكور في الرابط) الموضوعين في كائن الـ URL.
public String getProtocol() ترجع إسم البروتوكول الذي يستخدمه كائن الـ URL للإتصال بالشبكة.
public int getPort() ترجع رقم البورت الخاص بالبروتوكول الذي يستخدمه كائن الـ URL للإتصال بالشبكة.
ترجع 1- في حال عدم تحديد رقم البورت.
public int getDefaultPort() ترجع رقم البورت الإفتراضي للبروتوكول الذي يستخدمه كائن الـ URL للإتصال بالشبكة.
ترجع 1- في حال عدم وجود رقم بورت إفتراصي.
public String getPath() ترجع نص يمثل مسار الملف المراد الوصول إليه عن طريق كائن الـ URL.
ترجع نص فارغ في حال كان مسار الملف غير محدد.
public String getFile() ترجع نص يمثل مسار الملف المراد الوصول إليه عن طريق كائن الـ URL + الـ Query (في حال كان يوجد Query في الرابط).
في حال عدم وجود Query في الرابط, تعطي نفس نتيجة الدالة getPath().
ترجع نص فارغ في حال كان المسار غير محدد في الرابط.
public String getQuery() ترجع جزئية الـ Query الموضوعة في كائن الـ URL.
ترجع null في حال عدم وجود Query في كائن الـ URL.
public String getRef() ترجع جزئية الـ Anchor في كائن الـ URL.
Anchor تعني عنصر محدد موجود في صفحة الويب, و يقال له مرجع الصفحة.
لتحديد الـ Anchor في كائن الـ URL, يجب وضعه بعد إسم الملف ( الذي شير إلى صفحة محددة في الويب ) و نضع الرمز # قبله.

مثال
هذا الرابط يفتح الصفحة الأولى في المجلد الذي يحتوي على دروس جافا و يضعك مباشرةً في الفقرة الثانية.
https://harmash.com/java/java-overview.php#article_2
public String getUserInfo() ترجع معلومات المستخدم الموضوعة في كائن الـ URL في حال كانت ظاهرة فيه.
ترجع null في حال كان لا يوجد أي معلومة خاصة بالمستخدم في كائن الـ URL.

أمثلة
  • http://info@hamash.com
    هذا رابط يحتوي على إيميل شخص من موقع harmash.com, إذاً الدالة هنا ترجع info.

  • http://info:1234@hamash.com
    هذا رابط يحتوي على إيميل و كلمة مرور شخص من موقع harmash.com, إذاً الدالة هنا ترجع info:123.


ملاحظة: هذه مجرد أمثلة, كملة المرور يجب عدم إظهارها أبداً في الرابط حتى لا يتم سرقة الحساب.

public String toExternalForm() ترجع محتوى كائن URL كنص واحد نوعه String.
public String toString() ترجع محتوى كائن URL كنص واحد نوعه String.
public boolean equals(Object obj) تقارن كائن الـ URL الذي قام باستدعائها مع الكائن الذي نمرره لها كـ Argument.
إذاً تقارن الرابط الذي قام باستدعائها مع الرابط الذي نمرره لها كـ Argument.
ترجع true في حال كانا متطابقين.
public boolean sameFile(URL otherURL) تقارن كائن الـ URL الذي قام باستدعائها مع كائن الـ URL الذي نمرره لها كـ Argument.
إذاً تقارن الرابط الذي قام باستدعائها مع الرابط الذي نمرره لها كـ Argument.
ترجع true في حال كانا متطابقين.
public InputStream openStream()
throws IOException
تنشئ إتصال مباشر بين البرنامج و الملف الموجود في الموقع الذي يشير له كائن الـ URL.
في حال نجح الإتصال, ترجع كائن نوعه InputStream يتيح لك قراءة محتوى هذا الملف فقط.
في حال فشل الإتصال يتم رمي إستثناء.
public URLConnection openConnection()
throws IOException
تنشئ إتصال مباشر بين البرنامج و الملف الموجود في الموقع الذي يشير له كائن الـ URL.
في حال نجح الإتصال, ترجع كائن نوعه URLConnection يتيح لك قراءة محتوى هذا الملف أو الكتابة فيه.
في حال فشل الإتصال يتم رمي إستثناء.
public URLConnection openConnection(Proxy proxy).
throws IOException
تنشئ إتصال بين البرنامج و الملف الموجود في الموقع الذي يشير له كائن الـ URL عن طريق Proxy Server.
إذاً هنا يوجد طرف ثالث يلعب دور الوسيط بين البرنامج الذي يحاول الوصول للملف و الموقع الذي يملك الملف.
  • في حال نجح الإتصال, ترجع كائن نوعه URLConnection يتيح لك قراءة محتوى هذا الملف أو الكتابة فيه.

  • في حال كان البروتوكول المستخدم لا يعمل مع Proxy Server, يتم تجاهل Proxy Server و يتم وصل طرفي الإتصال مباشرةً مع بعضهما.

  • في حال وضعت الثابت Proxy.NO_PROXY كـ Argument, أيضاً يتم تجاهل Proxy Server و يتم وصل طرفي الإتصال مباشرةً مع بعضهما.

  • في حال حدث خطأ بسبب الملف يتم رمي الإستثناء IOException

  • في حال كان المستخدم لا يملك صلاحية لإستخدام الـ Proxy Server, يتم رمي الإستثناء SecurityException

  • في حال وضعنا null كـ Argument أو لم نحدد الـ Proxy Server بشكل صحيح, يتم رمي الإستثناء IllegalArgumentException

أمثلة شاملة


المثال الأول

في هذا المثال  قمنا بتعريف كائن نوعه URL يشير إلى رابط درس معين من موقعنا, ثم حاولنا عرض كافة المعلومات التي يمكن استخراجها من الرابط.

ملاحظة: تعمدنا عرض جميع المعلومات التي يمكن استخراجها من الرابط سواء كانت موجودة أو غير موجودة, لاحظ أن أي معلومة حاولنا عرضها و كانت غير موجودة في الرابط ستوضع القيمة null أو 1- مكانها كما ذكرنا في الجدول السابق.

Main.java
                    import java.net.URL;    // URL هنا قمنا باستدعاء الكلاس

	  public class Main {

	  public static void main(String[] args) {

	  // الآن سنحاول إجراء إتصال عبر الشبكة للوصول إلى ملف محدد
	  try
	  {
	  // يشير إلى رابط الدرس الأول في دورة جافا و بالتحديد الفقرة الثانية في الدرس url إسمه URL هنا قمنا بتعريف كائن نوعه
	  URL url = new URL("https://harmash.com:443/java/java-overview/index.php#article_2");

	  // هنا قمنا بعرض المعلومات المتوفرة و الغير متوفرة في الرابط
	  System.out.println("URL          : " + url.toString());
	  System.out.println("Protocol     : " + url.getProtocol());
	  System.out.println("Authority    : " + url.getAuthority());
	  System.out.println("Host name    : " + url.getHost());
	  System.out.println("File name    : " + url.getFile());
	  System.out.println("Path         : " + url.getPath());
	  System.out.println("Port         : " + url.getPort());
	  System.out.println("Default Port : " + url.getDefaultPort());
	  System.out.println("Reference    : " + url.getRef());
	  System.out.println("Query        : " + url.getQuery());
	  System.out.println("User Info    : " + url.getUserInfo());
	  }
	  catch( Exception e ) {
	  e.printStackTrace();
	  }

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    URL          : https://harmash.com:80/java/java-overview/index.php#article_2
	  Protocol     : https
	  Authority    : harmash.com:80
	  Host name    : harmash.com
	  File name    : /java/java-overview/index.php
	  Path         : /java/java-overview/index.php
	  Port         : 80
	  Default Port : 443
	  Reference    : article_2
	  Query        : null
	  User Info    : null
	


المثال الثاني

في هذا المثال  قمنا بتعريف كائن نوعه URL يشير إلى رابط درس معين من موقعنا.
بعدها قمنا باستدعاء الدالة openStream() و التي ترجع كائن نوعه URLConnection يتيح لنا قراءة محتوى الملف الذي يشير إليه الرابط.
كائن الـ URLConnection قمنا بتخزينه في كائن نوعه InputStream حتى نستطيع قراءة محتوى الملف في البرنامج.

في الأخير قمنا بإنشاء حلقة while لقراءة محتوى الملف و تخزينه في متغير نوعه String إسمه data.

ملاحظة: هنا ستتعلم طريقة قراءة محتوى أي ملف من موقع ما مهما كان نوعه سواء صورة, فيديو ملف نصي.

Main.java
                    import java.net.URL;               // URL هنا قمنا باستدعاء الكلاس
	  import java.io.InputStream;        // InputStream هنا قمنا باستدعاء الكلاس

	  public class Main {

	  public static void main(String[] args) {

	  // قمنا بتجهيز هذا المتغير من أجل تخزين محتوى الملف فيه
	  String data = "";

	  // الآن سنحاول إجراء إتصال عبر الشبكة للوصول إلى ملف محدد و قراءة محتواه
	  try
	  {
	  // يشير إلى رابط الدرس الأول في دورة جافا url إسمه URL هنا قمنا بتعريف كائن نوعه
	  URL url = new URL("https://harmash.com/java/java-overview/index.php");

	  // is في الكائن URL هنا قمنا بتخزين الملف الذي يشير له الكائن
	  InputStream is = url.openStream();

	  // data و تخزنهم في المتغير is هنا قمنا بإنشاء حلقة تمر على جميع الأحرف الموجودة في الكائن
	  int c;
	  while ((c = is.read()) != -1)
	  {
	  data += (char)c;
	  }

	  // هنا قمنا بإغلاق الإتصال مع الملف
	  is.close();

	  // هنا قمنا بعرض محتوى الملف
	  System.out.println(data);
	  }
	  catch( Exception e ) {
	  e.printStackTrace();
	  }

	  }

	  }
	

عند تشغيل البرنامج سيتم عرض محتوى الصفحة harmash.com/java/java-networking/, أي كود الصفحة التي يشير لها الرابط.

لاحظ أن الأحرف العربية لم تظهر بشكل صحيح, هذه المشكلة سببها عدم تحديد الترميز المستخدم في القراءة, و حللنا هذه المشكلة في المثال الثالث.

<!DOCTYPE html>
<html lang="ar">

<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Ø¯ÙØ±Ø© شاÙÙØ© ÙÙ ÙØºØ© Ø¬Ø§ÙØ§ تبدأ ÙØ¹Ù ÙÙ Ø§ÙØµÙر Ø­ØªÙ Ø§ÙØ¥Ø­ØªØ±Ø§Ù">
    <meta name="keywords" content="">
   
    <!-- Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-66898688-2"></script>
    <script>
    window.dataLayer = window.dataLayer || [];
...


المثال الثالث

هنا سنعيد نفس المثال الثاني من حيث الفكرة و لكننا إستخدمنا هذه المرة الـ Buffer عند قراءة محتوى الملف لتسريع عملية القراءة و بالتالي لأداء أفضل.

ملاحظة: هنا ستتعلم طريقة قراءة محتوى أي ملف نصي من موقع ما مع مراعاة أي نوع ترميز مستخدم في الملف.

المثال الثالث

Main.java
                    import java.net.URL;                       // URL هنا قمنا باستدعاء الكلاس
	  import java.io.BufferedReader;             // BufferedReader هنا قمنا باستدعاء الكلاس
	  import java.io.InputStream;                // InputStream هنا قمنا باستدعاء الكلاس
	  import java.io.InputStreamReader;          // InputStreamReader هنا قمنا باستدعاء الكلاس

	  public class Main {

	  public static void main(String[] args) {

	  // قمنا بتجهيز هذا المتغير من أجل تخزين محتوى الملف فيه
	  String data = "";

	  // الآن سنحاول إجراء إتصال عبر الشبكة للوصول إلى ملف محدد و قراءة محتواه
	  try
	  {
	  // يشير إلى رابط الدرس الأول في دورة جافا url إسمه URL هنا قمنا بتعريف كائن نوعه
	  URL url = new URL("https://harmash.com/java/java-overview/index.php");

	  // is في الكائن URL هنا قمنا بتخزين الملف الذي يشير له الكائن
	  InputStream is = url.openStream();

	  // Buffer لحل مشكلة الترميز و وضع محتوى الملف في الـ InputStreamReader إلى النوع is هنا قمنا بتحويل نوع الكائن
	  InputStreamReader isr = new InputStreamReader(is, "UTF-8");

	  // Buffer يتيح لنا قراءة محتوى الملف الموجود في الـ br الكائن
	  BufferedReader br = new BufferedReader(isr);

	  // data و تخزنهم في المتغير br هنا قمنا بإنشاء حلقة تمر على جميع الأحرف الموجودة في الكائن
	  int c;
	  while ((c = br.read()) != -1)
	  {
	  data += (char)c;
	  }

	  // هنا قمنا بإغلاق الإتصال مع الملف
	  is.close();
	  isr.close();
	  br.close();

	  // هنا قمنا بعرض محتوى الملف
	  System.out.println(data);
	  }
	  catch( Exception e ) {
	  e.printStackTrace();
	  }

	  }

	  }
	

عند تشغيل البرنامج سيتم عرض محتوى الصفحة harmash.com/java/java-networking/, أي كود الصفحة التي يشير لها الرابط.

<!DOCTYPE html>
<html lang="ar">

<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="دورة شاملة في لغة جافا تبدأ معك من الصفر حتى الإحتراف">
    <meta name="keywords" content="">
   
    <!-- Google Analytics -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-66898688-2"></script>
    <script>
    window.dataLayer = window.dataLayer || [];
...

 الكلاس URLConnection في جافا

مقدمة

الكلاس URLConnection عبارة عن كلاس مجرّد ( Abstract Class ) و هو يستخدم للقراءة و الكتابة في الملف الذي يشير له كائن الـ URL بالإضافة إلى أنه يمكن من خلاله معرفة و تحديد كافة المعلومات المتعلقة بإجراء الإتصال مع الملف.
فمثلاً يتيح لنا معرفة آخر مرة تم فيها تحديث الملف, تاريخ صلاحية الملف, إسم الكلاس المناسب لإرسال المعلومات منه و استقبالها فيه إلخ..


بناؤه

                  public abstract class URLConnection
	extends Object
  

حقول الملف ( File Header Fields )

أي ملف موجود في أي موقع, يمكن وضع معلومات خاصة فيه لا يراها المستخدم العادي, لكنها تكون مهمة جداً للبرنامج أو للسيرفر الذي يملك هذا الملف.
هذه المعلومات تفيد في عملية تحديث محتوى الملف, صلاحيات التعامل معه, نوعه, الترميز المتبع فيه إلخ..

جميع هذه المعلومات مع بعضها تسمى حقول الملف.
سنتعرف لاحقاً على دوال مصممة خصيصياً لإضافة حقول جديدة تحدد طريقة تعامل البرنامج مع الملف.


التعامل مع الملفات عن بعد

للوصول إلى الملفات عن بعد سواء لقراءة محتواها أو التعديل عليها, عليك اتباع الخطوات التالية:

  1. إنشاء كائن من الكلاس URL يمثل رابط لملف موجود في موقع معين.

  2. إستدعاء الدالة openConnection() و التي ترجع كائن نوعه URLConnection يسمح لنا بالتعامل مع هذا الملف.

  3. معرفة البروتوكول المستخدم في إجراء عملية الإتصال.

  4. تحويل كائن الـ URLConnection إلى كائن من إحدى الكلاسات التي ترث منه يكون مخصص للتعامل مع البروتوكول المستخدم في عملية الإتصال.



تحويل نوع كائن الـ URLConnection

الكلاس URLConnection عبارة عن كلاس مجرّد تم تصميمه لإنشاء إتصال مع الملف الذي يشير إليه كائن الـ URL مهما كان البروتوكول المستخدم في عملية الإتصال.
في حال أردت أن تتعامل مع هذا الملف, سيكون عليك تحويل نوع كائن الـ URLConnection لنوع يلائم البروتوكول المستخدم لإجراء هذا الإتصال.

إذاً نوع البروتوكول المستخدم لإجراء الإتصال هو الذي يحدد النوع الذي يجب تحويل كائن الـ URLConnection إليه, أمثلة:

  • في حال كان البروتوكول http هو المستخدم في إجراء الإتصال, سيكون عليك تحويل نوع الكائن إلى النوع HttpURLConnection.

  • في حال كان البروتوكول https هو المستخدم في إجراء الإتصال, سيكون عليك تحويل نوع الكائن إلى النوع HttpsURLConnection.

  • في حال كان البروتوكول ftp هو المستخدم في إجراء الإتصال, سيكون عليك تحويل نوع الكائن إلى النوع FtpURLConnection.

  • في حال كان البروتوكول jar هو المستخدم في إجراء الإتصال, سيكون عليك تحويل نوع الكائن إلى النوع JarURLConnection إلخ..

كونستركتورات الكلاس URLConnection

الكلاس URLConnection يملك الكونستركتور التالي فقط.

الكونستركتور مع تعريفه
protected URLConnection(URL url) ينشئ كائن نوعه URLConnection يمثل كافة المعلومات المتعلقة بالملف الذي يشير له كائن الـ URL, أي يمثل صلة الوصل بين البرنامج و الملف الذي يشير له كائن الـ URLكـ Argument.
بما أنه معرف كـ protected فهذا يعني أنه لا يمكن إستخدامه إلا في حال قمنا بإنشاء كلاس يرث منه و يفعل Override للدالة connect() الموجودة فيه.

ملاحظة: لست بحاجة إلى إستخدام هذا الكونستركتور لأنك لن تقوم ببناء كلاس خاص للتعامل مع الشبكات.

دوال الكلاس URLConnection

الجدول التالي يحتوي على دوال الكلاس URLConnection.

الدالة مع تعريفها
public String getContent() ترجع كائن يمثل إسم الكلاس الذي يجب استخدامه للتعامل مع محتوى الملف الذي يشير إليه كائن الـ URLConnection.
في حال حدث خطأ بسبب الملف يتم رمي الإستثناء IOException
و في حال كان البروتوكول المستخدم غير ملائم للتعامل مع الملف, يتم رمي الإستثناء UnknownServiceException.
public String getContentEncoding() ترجع نص يمثل قيمة الحقل content-encoding في حال تم تحديدها في كائن الـ URLConnection.
و ترجع null في حال كانت قيمته غير معروفة.

معلومة: هذا الحقل يحدد الطريقة التي سيتم فيها تجزيء الملف عند إرساله إلى الجهة المطلوبة, و هو عبارة عن إسم الترميز المستخدم من قبل البروتوكول المعنمد عليه للتعامل مع هذا الملف.
public long getContentLengthLong() ترجع نص يمثل قيمة الحقل content-encoding في حال تم تحديدها في كائن الـ URLConnection.
و ترجع 1- في حال كانت غير محددة.

معلومة: هذا الحقل يمثل حجم البيانات الأساسية المتعلقة بهذا الإتصال في حال قمت بتحديده, أي عدد الأحرف المتوقع إستقبالها.
طبعاً لا ضرورة لتحديدها لأنه سيقوم بذلك بشكل تلقائي في حال لم تفعل ذلك.
public String getContentType() ترجع نص يمثل قيمة الحقل Content-Typeو هو عبارة نوع الملف الذي يشير إليه كائن الـ URLConnection.
و ترجع null في حال كان النوع غير معروف.
public int getDate() ترجع قيمة الحقل date, و هو عبارة عن رقم يمثل التاريخ و الوقت الذي قام فيه السيرفر بإرسال الملف الذي يشير إليه كائن الـ URLConnection.
الرقم الذي ترجعه عبارة عن عدد الـ milliseconds الموجودة إعتباراً من التاريخ 1/1/1970 00:00:00 GMT.
ترجع 0 في حال كان الوقت غير معروف.
public int getLastModified() ترجع قيمة الحقل last-modified في حال كانت مذكورة في الملف الذي يشير إليه كائن الـ URLConnection, و هو عبارة عن رقم يمثل تاريخ آخر مرة تم فيها تعديل هذا الملف.
الرقم الذي ترجعه عبارة عن عدد الـ milliseconds الموجودة إعتباراً من التاريخ 1/1/1970 00:00:00 GMT.
ترجع 0 في حال كان الوقت غير معروف.
public long getExpiration() ترجع قيمة الحقل expires في حال كانت مذكورة في الملف الذي يشير إليه كائن الـ URLConnection, و هو عبارة عن رقم يمثل تاريخ صلاحية الملف.
الرقم الذي ترجعه عبارة عن عدد الـ milliseconds الموجودة إعتباراً من التاريخ 1/1/1970 00:00:00 GMT.
ترجع 0 في حال كانت غير مذكورة في الملف.

معلومة: في العادة أي ملف نطلبه من موقع ما يتم إعطائه تاريخ صلاحية, هذا التاريخ يضمن لك أن زوار الموقع سيحصلون دائماً على آخر تحديث فعلته لهذا الملف.
فمثلاً, عندما تقوم بفتح أي صفحة من النت, يقوم المتصفح بحفظ هذه الصفحة على القرص الصلب, و في حال طلبت نفس الصفحة من جديد حتى و لو كان بعد سنة, سيقوم المتصفح أولاً بالبحث في الصفحات المخزنة فيه سابقاً, و في حال وجد أن هذه الصفحة أصلاً موجودة عنده, يعرضها لك بسرعة بدل أن تنتظره ليحملها لك من النت. إذاً هنا المشكلة تكون أنك ستحصل دائماً على النسخة القديمة للصفحة.
الحل لهذه المشكلة كان بإعطاء الملفات تاريخ صلاحية. فمثلاً تجعل الصفحة صالحة لمدة يوم واحد, عندها في إذا قام المستخدم بطلب نفس الصفحة التي طلبها في اليوم السابق, سيجد المتصفح أن الصفحة التي يمكلها إنتهت صلاحيتها و بالتالي سيطلبها له من السيرفر من جديد و عندها سيحصل على آخر تحديث تم إجراءه على الصفحة.
إذاً هذا الحقل مهم جداً و يجب ذكره في جميع الملفات التي ترفع في النت.
public long getIfModifiedSince() ترجع قيمة الحقل ifModifiedSince في حال كانت مذكورة في الملف, و هو عبارة عن رقم يمثل تاريخ آخر مرة علم فيها المستخدم أن الملف قد تم تحديثه.
الرقم الذي ترجعه عبارة عن عدد الـ milliseconds الموجودة إعتباراً من التاريخ 1/1/1970 00:00:00 GMT.
ترجع 0 في حال كان الوقت غير معروف.

معلومة: هنا مثلاً يتم تصميم البرنامج لمقارنة آخر مرة قام فيها هو بتعديل محتوى الملف مع آخر مرة قام فيها أي أحد بتعديل محتوى هذا الملف.
في حال وجد نفسه هو آخر من قام بتحديث الملف, يعلم عندها أن المعلومات المذكورة فيه لم تتغير و بالتالي يمكنه إستعمال النسخة التي يملكها من هذا الملف بدل تحميلها من النت من جديد.
لكن في حال وجد أن هذا الملف قد تم تحديثه من قبل شخص آخر, عندها سيقوم بتحميل النسخة الموجودة في السيرفر من جديد و تجاهل النسخة التي يملكها.
public void setDoInput(boolean input) تحدد قيمة الحقل doinput في كائن الـ URLConnection.
إذا قمت بتمرير القيمة true لها فهذا يعني أنك تنوي استخدام كائن الـ URLConnection لقراءة محتوى الملف.
ترمي الإستثناء IllegalStateException في حال كان الكائن متصلاً بالملف قبل استدعائها.
public boolean getDoInput() ترجع قيمة الحقل doinput في حال قمنا بتحديدها في كائن الـ URLConnection.
public void setDoOutput(boolean output) تحدد قيمة الحقل doinput في كائن الـ URLConnection.
إذا قمت بتمرير القيمة true لها فهذا يعني أنك تنوي استخدام كائن الـ URLConnection للكتابة في الملف.
ترمي الإستثناء IllegalStateException في حال كان الكائن متصلاً بالملف قبل استدعائها.
public boolean getDoOutput() ترجع قيمة الحقل dooutput في حال قمنا بتحديدها في كائن الـ URLConnection.
public void setUseCaches(boolean usecaches) تحدد قيمة الحقل usecaches في كائن الـ URLConnection.
إذا قمت بتمرير القيمة true لها فهذا يعني أنك تريد الإحتفاظ بنسخة من الملف في القرص الصلب.
ترمي الإستثناء IllegalStateException في حال كان الكائن متصلاً بالملف قبل استدعائها.
public boolean getUseCaches() ترجع قيمة الحقل usecaches في حال قمنا بتحديدها في كائن الـ URLConnection.
public InputStream getInputStream() ترجع كائن نوعه InputStream يستخدم للقراءة من الملف.
في حال حدث خطأ بسبب الملف يتم رمي الإستثناء IOException.
و في حال كان البروتوكول المستخدم غير ملائم للتعامل مع الملف, يتم رمي الإستثناء UnknownServiceException.
public OutputStream getOutputStream() ترجع كائن نوعه OutputStream يستخدم للكتابة في الملف.
في حال حدث خطأ بسبب الملف يتم رمي الإستثناء IOException.
و في حال كان البروتوكول المستخدم غير ملائم للتعامل مع الملف, يتم رمي الإستثناء UnknownServiceException.
public String getHeaderField(String name) ترجع قيمة أي حقل نمرر إسمه لها كنص.
public Map<String,List<String>> getHeaderFields() ترجع كائن نوعه Map يحتوي على جميع أسماء و قيم الحقول الموجودة في Header كائن الـ Connection بشكل key / value.

معلومة: هذه الدالة ترجع لك جميع معلومات الإتصال دفعة واحدة كجدول, و بالتالي تسهل عليك جلب جميع المعلومات المتعلقة بعملية الإتصال مع الملف.
public URL getURL() ترجع كائن الـ URL المستخدم من قبل كائن الـ Connection.

أمثلة شاملة


المثال الأول

في هذا المثال  قمنا بتعريف كائن نوعه URL يشير إلى صفحتنا في فيسبوك https://www.facebook.com/harmashcom.
بعدها قمنا باستدعاء الدالة openConnection() و التي ترجع كائن نوعه URLConnection يتيح لنا تجهيز معلومات الإتصال.
بعدها قمنا باستدعاء الدالة getHeaderFields() و التي ترجع كائن نوعه Map يمثل جميع الحقول الموجودة في كائن الـ URLConnection.
بعدها قمنا بإنشاء حلقة For Each تعرض لنا في كل دورة معلومات حقل واحد, أي Entry واحد.

ملاحظة: الإنترفيس Map يملك Inner Interface إسمه Entry نستخدمه للتعامل مع العناصر التي يملكها كائن الـ Map.
بما أن الإنترفيس Entry معرف بداخل الإنترفيس Map يمكننا إستدعاءه هكذا Map.Entry.

Main.java
                    import java.net.URL;                  // URL هنا قمنا باستدعاء الكلاس
	  import java.net.URLConnection;        // URLConnection هنا قمنا باستدعاء الكلاس
	  import java.util.Map;                 // Map هنا قمنا باستدعاء الكلاس
	  import java.util.List;                // List هنا قمنا باستدعاء الكلاس

	  public class Main {

	  public static void main(String[] args) {

	  // الآن سنحاول إجراء إتصال عبر الشبكة للوصول إلى ملف محدد و عرض كافة معلومات الإتصال المتوفرة
	  try
	  {
	  // يشير إلى رابط موقعنا في فيسبوك url إسمه URL هنا قمنا بتعريف كائن نوعه
	  URL url = new URL("https://www.facebook.com/harmashcom");

	  // connection إسمه URLConnection هنا قمنا بتعريف كائن نوعه
	  URLConnection connection = url.openConnection();

	  // map في الكائن getHeaderFields() هنا قمنا بتخزين أسماء و قيم الحقول التي سترجعها الدالة
	  Map<String, List<String>> map = connection.getHeaderFields();

	  // و بعدها سيتم عرضهم entry بجلب إسم حقل و قيمته و وضعهم في الكائن entrySet() هنا في كل دورة تقوم الدالة
	  for (Map.Entry<String, List<String>> entry : map.entrySet())
	  {
	  System.out.println(entry.getKey() +" : "+ entry.getValue());
	  }

	  }
	  catch( Exception e ) {
	  System.out.println(e.getMessage());
	  }

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل.

                    null : [HTTP/1.1 200 OK]
	  Expires : [Sat, 01 Jan 2000 00:00:00 GMT]
	  X-XSS-Protection : [0]
	  Set-Cookie : [fr=0R31HwmcA2qNYUaC..BX7LPo.cm.AAA.0.0.BX7LPo.AWWl-1aI; expires=Wed, 27-Dec-2016 06:25:44 GMT;
	  Max-Age=7776000; path=/; domain=.facebook.com; httponly]
	  Connection : [keep-alive]
	  public-key-pins-report-only : [max-age=500; pin-sha256="WoiWRyIOVNa9ihaBciRSC7XHjliasfasTH32Us5sud4PB18="; pin-sha256="r/mIkG3eEpVdm+u/ko/cwxzOMo1bJ4TyHIlByibiA5E=";
	  pin-sha256="q4PO2G4cbkZhZ82+JgmRUyGMoAeozA+BSXVXQWA8XWQ="; report-uri="http://reports.fb.com/hpkp/"]
	  Cache-Control : [private, no-cache, no-store, must-revalidate]
	  X-Content-Type-Options : [nosniff]
	  Pragma : [no-cache]
	  X-FB-Debug : [k1P7SDLc7cblje32GG3ydHZ8awX/he/64uiyasdf34f4BF4HYuiXpPSGYahNfpx+7F+vnKq3z4IbNvEEXYjgg==]
	  X-Frame-Options : [DENY]
	  Strict-Transport-Security : [max-age=15552000; preload]
	  Vary : [Accept-Encoding]
	  Date : [Thu, 29 Sep 2016 06:25:44 GMT]
	  Transfer-Encoding : [chunked]
	  P3P : [CP="Facebook does not have a P3P policy. Learn why here: http://fb.me/p3p"]
	  Content-Type : [text/html]
	


المثال الثاني

في هذا المثال  قمنا بتعريف برنامجين, الأول هو برنامج صغير جداً مكتوب بلغة PHP موجود على السيرفر أي في موقع ما, و الثاني عبارة عن برنامج مكتوب بلغة جافا. أنت الآن مسؤول عن كود الجافا فقط, لأنك تدرس لغة جافا و ليس لغة PHP التي تستعمل في بناء المواقع.

فكرة البرنامج هي الوصول إلى ملف موجود في سيرفر معين عبر شبكة النت و إدخال بيانات فيه إضافة إلى البيانات الموجودة فيه, بعدها قمنا بقراءة محتوى هذا الملف.

  • من السطر 24 إلى السطر 41, قمنا بإرسال البيانات التي أدخلها المستخدم إلى الملف الموجود في السيرفر.

  • من السطر 44 إلى السطر 60, قمنا باستقبال كامل البيانات التي أصبحت موجودة في الملف و من ثم عرضناها.

هذا ملف PHP و هو عبارة عن صفحة ويب موجودة على السيرفر, نمرر لها إيميل و كلمة مرور عند استدعائها, فتعرضهم في الصفحة عندما تفتح.
إنتبه: لم نشرح هذا الكود لأنك الآن تدرس لغة جافا و ليس لغة PHP و لست بحاجة إلى فهمه أيضاً.

test.php
                    <?php
	  if(isset($_GET['email']) &↦ isset($_GET['password']))
	  {
	  $email = $_GET['email'];
	  $password = $_GET['password'];
	  echo "your email is '$email' and your password is '$password'";
	  }
	  ?>
	

Main.java
                    import java.io.BufferedReader;              // BufferedReader هنا قمنا باستدعاء الكلاس
	  import java.io.InputStreamReader;           // InputStreamReader هنا قمنا باستدعاء الكلاس
	  import java.net.HttpURLConnection;          // HttpURLConnection هنا قمنا باستدعاء الكلاس
	  import java.net.URL;                        // URL هنا قمنا باستدعاء الكلاس
	  import java.util.Scanner;                   // Scanner هنا قمنا باستدعاء الكلاس

	  public class Main {

	  public static void main(String[] args) {

	  // سنستخدمه لإستقبال بيانات في البرنامج من لوحة مفاتيح المستخدم input إسمه Scanner هنا قمنا بإنشاء كائن من الكلاس
	  Scanner input = new Scanner(System.in);

	  // لتخزين البيانات التي سيطلب البرنامج من المستخدم إدخالها password و email هنا قمنا بتجهيز المتغيرات
	  String email;
	  String password;

	  // الآن سنحاول إجراء إتصال عبر الشبكة للوصول إلى ملف محدد و إدخال بيانات فيه ثم إسترجاع جميع البيانات التي فيه
	  try
	  {
	  // email هنا سيطلب من المستخدم إدخال بريده الإلكتروني و بعدها سيتم تخزينه في المتغير
	  System.out.print("Enter you email: ");
	  email = input.next();

	  // password هنا سيطلب من المستخدم إدخال كلمة المرور و بعدها سيتم تخزينها في المتغير
	  System.out.print("Enter you password: ");
	  password = input.next();

	  // و يمرر له المعلومات التي أدخلها المتسخدم test.php هنا قمنا بإنشاء رابط يوصلنا إلى الملف
	  String urlLink = "http://localhost/java/test.php?email=" +email+ "&password=" +password;

	  // urlLink يشير إلى الرابط url إسمه URL هنا قمنا بتعريف كائن نوعه
	  URL url = new URL(urlLink);

	  // conn و وضعناه في الكائن HttpURLConnection إلى النوع openConnection() الذي سترجعه الدالة URLConnection هنا قمنا بتحويل كائن الـ
	  HttpURLConnection conn = (HttpURLConnection)url.openConnection();

	  // هنا حددنا الطريقة التي سيتم فيها إرسال البيانات إلى السيرفر
	  conn.setRequestMethod("GET");

	  // و الذي يسمح لنا بقراءة محتوى الملف br من السيرفر في الكائن getInputStream() هنا قمنا بتخزين الملف الذي سترجعه الدالة
	  BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

	  // لجلب البيانات الموجودة بشكل مؤقت line لحفظ البيانات التي أرجعها السيرفر, و المتغير data هنا قمنا بتجهيز المتغير
	  String data = "";
	  String line;

	  // data غير فارغ سيتم قراءة محتوياته سطرا سطرا و تخزينهم في المتغير br هنا قلنا أنه طالما أن الملف الذي يشير له الكائن
	  while((line = br.readLine()) != null)
	  {
	  data += line;
	  }

	  br.close();

	  // data هنا قمنا بعرض محتوى الملف الذي أرجعه السيرفر و الذي قمنا بتخزينه في المتغير
	  System.out.println("\nServer Response");
	  System.out.println(data);

	  }
	  catch( Exception e ) {
	  System.out.println(e.getMessage());
	  }

	  }

	  }
	

سنحصل على النتيجة التالية عند التشغيل إذا قمنا بإدخال نفس المعلومات التي أدخلناها عند تشغيل البرنامج.
قمنا بتعليم البيانات التي إنتظرنا البرنامج لإدخالها من لوحة المفاتيح باللون الأزرق.
و قمنا بتعليم البيانات التي حصلنا عليها من السيرفر باللون الأخضر.

Enter you email: contact@harmash.com
Enter you password: 1234

Server Response
your email is 'contact@harmash.com' and your password is '1234'

 الكلاس InetAddress في جافا

مقدمة

الكلاس InetAddress مصمم لتحليل عنوان الجهاز أو الموقع الذي نحاول الإتصال به عن بعد عبر شبكة النت.


بناؤه

                  public class InetAddress
	extends Object
	implements Serializable
  

مصطلحات تقنية

  • :IP Address تعني عنوان الجهاز أو الموقع على الشبكة. فمثلاً عنوان harmash.com هو 108.179.232.152.

  • :Host Name تعني أي إسم الجهاز أو الموقع على الشبكة. فمثلاً العنوان 108.179.232.152 يشير إلى الموقع harmash.com.

  • :Local Host تعني عنوان الجهاز على الشبكة المحلية. في العادة يكون يشبه هذا العنوان 192.168.1.105.

  • :Loopback Address تعني العنوان الذس يستخدمه الجهاز لفحص كرت الشبكة. و هو 127.0.0.1.

  • :Multicast Address تعني العنوان الذس يستخدمه الجهاز لإرسال البيانات إلى عدة أجهزة موجودة معه على الشبكة المحلية. مثل العنوان 224.0.0.1.

دوال الكلاس InetAddress

الجدول التالي يحتوي على دوال الكلاس InetAddress.


أخطاء محتملة

بعض الأخطاء التي قد تظهر لك عند استخدام دوال الكلاس InetAddress.

  • ترمي الإستثناء UnknownHostException في حال لم يتم معرفة الـ IP Address للـ Host Name الذي قمنا بتمريره.

  • ترمي الإستثناء SecurityException في حال كان البرنامج ممنوع من الإتصال بالشبكة.


الدالة مع تعريفها
public static InetAddress getByName(String host) نعطيها نص عبارة عن Host Name, مثل www.harmash.com فترجعه ككائن نوعه InetAddress.
public static InetAddress getByAddress(byte[] addr) نعطيها مصفوفة نوعها byte تمثل IP Address فترجعه ككائن نوعه InetAddress.
public static InetAddress getByAddress(String host, byte[] addr) نعطيها مصفوفة نوعها byte تمثل IP Address و Host Name فترجعه ككائن نوعه InetAddress.
public static InetAddress[] getAllByName(String host) ترجع مصفوفة نوعها InetAddress تحتوي على جميع العناوين التي يملكها الموقع الذي نمررها له كـ Argument.
ملاحظة: المواقع الضخمة مثل google و facebook, يملكون أكثر من عنوان.
public static InetAddress getLocalHost() ترجع كائن نوعه InetAddress عبارة عن إسم و عنوان الجهاز الذي قام باستدعاءها على الشبكة المحلية.
public static InetAddress getLoopbackAddress() ترجع كائن نوعه InetAddress عبارة عن العنوان الذي يستخدمه الجهاز لفحص كرت الشبكة.
public String getHostAddress() ترجع الـ Host Name الذي يشير له كائن الـ InetAddress كنص.
public byte[] getAddress() ترجع عنوان الموقع الذي يشير له كائن الـ InetAddress الذي قام باستدعائها كمصفوفة نوعها byte.
public String getHostName() ترجع الـ IP Address الذي يشير له كائن الـ InetAddress كنص.
public boolean isAnyLocalAddress() ترجع true في حال كان كائن الـ InetAddress الذي قام باستدعائها عبارة عن Local Address.
public boolean isLoopbackAddress() ترجع true في حال كان كائن الـ InetAddress الذي قام باستدعائها عبارة عن Loopback Address.
public boolean isMulticastAddress() ترجع true في حال كان كائن الـ InetAddress الذي قام باستدعائها عبارة عن Multicast Address.
public String toString() ترجع الـ Host Name الذي يشير له كائن الـ InetAddress.
public boolean equals(Object obj) تقارن كائن الـ InetAddress الذي قام باستدعائها مع الكائن الذي نمرره لها كـ Argument.
ترجع true في حال كانا متطابقين.
public int hashCode() ترجع الـ Hash Code لكائن الـ InetAddress الذي قام باستدعائها.

مثال شامل

في المثال التالي قمنا بإنشاء كائن نوعه InetAddress إسمه IP يشير إلى موقع harmash.com.
بعدها قمنا بتحليل إسم هذا الموقع لمعرفة كافة المعلومات المتعلقة بعنوانه.


Main.java
                    import java.net.InetAddress;    // InetAddress هنا قمنا باستدعاء الكلاس

	  public class Main {

	  public static void main(String[] args) {

	  // الآن سنحاول إجراء إتصال عبر الشبكة للوصول إلى موقع محدد
	  try
	  {
	  // harmash.com يشير إلى موقع ip إسمه InetAddress لتعريف كائن نوعه getByName() هنا قمنا باستدعاء الدالة
	  InetAddress ip = InetAddress.getByName("www.harmash.com");

	  // harmash.com هنا قمنا بعرض إسم و عنوان موقع
	  System.out.println("Host name  : " + ip.getHostName());
	  System.out.println("IP address : " + ip.getHostAddress());

	  // Loopback Address و الـ Local Host هنا قمنا بعرض
	  System.out.println("Local Host : " + InetAddress.getLocalHost());
	  System.out.println("Loopback   : " + InetAddress.getLoopbackAddress());
	  }
	  catch( Exception e ) {
	  System.out.println(e.getMessage());
	  }

	  }

	  }
	

ستحصل على نتيجة تشبه النتيجة التالية عند التشغيل.

                    Host name  : www.harmash.com
	  IP address : 108.179.232.152
	  Local Host : Mhamad-PC/192.168.88.1
	  Loopback   : localhost/127.0.0.1
	

 الكلاس Socket و الكلاس ServerSocket في جافا

مقدمة

لإجراء إتصال بين جهازي كمبيوتر متصلين مع بعضهما البعض على شبكة النت, نقوم بتجهيز Socket في الكمبيوتر الأول و Socket في الكمبيوتر الثاني.
الـ Socket هي المعلومات الأساسية التي يحتاج إليها البرنامج للتواصل مع برنامج موجود في كمبيوتر آخر.

للتعامل مع الـ Socket جافا توفر لنا مجموعة من الكلاسات التي يمكننا إستخدامها لإنشاء برامج تجعل المستخدمين قادرين على التواصل فيما بينهم بشكل متزامن عبر شبكة النت كما في الصورة التالية.


في هذا الدرس سنشرح الكلاسات Socket و ServerSocket التي يمكن استخدامها لمشاركة البيانات بين أي أجهزة كمبيوتر متصلة بشبكة النت.


مصطلحات تقنية

  • Server: تعني خادم أو سيرفر في العربية, و هو عبارة عن كمبيوتر له مواصفات عالية, يقدم خدمات للـ Clients.

  • Client: تعني عميل أو مستخدم عادي في العربية, و هو عبارة عن أي كمبيوتر عادي يحاول أن يتواصل مع الـ Server للحصول على خدمة منه.

  • Remote App: تعني إنشاء تطبيق يستخدم للتحكم في جهاز ما عن بعد و كأن الشخص يجلس أمامه.

الكلاس Socket

الكلاس Socket يستخدم لإنشاء إتصال بين برنامج المستخدم و البرنامج الموجود في السيرفر.


بناؤه

                  public class Socket
	extends Object
	implements Closeable 
  


كونستركتورات الكلاس Socket

الجدول التالي يحتوي على بعض الكونستركتورات الموجودة في الكلاس Socket.

الكونستركتور مع تعريفه
public Socket(String host, int port) ينشئ كائن نوعه Socket يجعل برنامج المستخدم متصلاً مع السيرفر بشكل متزامن.

باراميترات
  • host هو نص يمثل عنوان السيرفر الذي سنحاول الإتصال به من برنامج المستخدم.

  • port هو رقم يمثل البورت الذي سيتم حجزه خصيصاً للبرنامج في جهاز المستخدم.

public Socket(InetAddress address, int port) ينشئ كائن نوعه Socket يجعل برنامج المستخدم متصلاً مع السيرفر بشكل متزامن.

باراميترات
  • host هو كائن نوعه InetAddress يمثل عنوان السيرفر الذي سنحاول الإتصال به من برنامج المستخدم.

  • port هو رقم يمثل البورت الذي سيتم حجزه خصيصاً للبرنامج في جهاز المستخدم.

public Socket(String host, int port, InetAddress localAddr, int localPort) ينشئ كائن نوعه Socket يجعل برنامج المستخدم متصلاً مع السيرفر بشكل متزامن.
يستخدم في حالة الـ Remote App.

باراميترات
  • host هو نص يمثل عنوان السيرفر الذي سنحاول الإتصال به من برنامج المستخدم.

  • port هو رقم يمثل البورت المحجوز في السيرفر خصيصاً للبرنامج الذي سنحاول الإتصال به من برنامج المستخدم.

  • localAddr هو كائن نوعه InetAddress يمثل عنوان كمبيوتر المستخدم الذي يحاول الإتصال بالسيرفر.

  • localPort هو رقم يمثل البورت الذي سيتم حجزه خصيصاً للبرنامج في جهاز المستخدم.

public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) ينشئ كائن نوعه Socket يجعل برنامج المستخدم متصلاً مع السيرفر بشكل متزامن.
يستخدم في حالة الـ Remote App.

باراميترات
  • address هو كائن نوعه InetAddress يمثل عنوان السيرفر الذي سنحاول الإتصال به من برنامج المستخدم.

  • port هو رقم يمثل البورت المحجوز في السيرفر خصيصاً للبرنامج الذي سنحاول الإتصال به من برنامج المستخدم.

  • localAddr هو كائن نوعه InetAddress يمثل عنوان كمبيوتر المستخدم الذي يحاول الإتصال بالسيرفر.

  • localPort هو رقم يمثل البورت الذي سيتم حجزه خصيصاً للبرنامج في جهاز المستخدم.

public Socket() ينشئ كائن نوعه Socket لكنه لا يجعل برنامج المستخدم متصلاً مع السيرفر.
هنا ستضطر أن تستدعي الدالة connect() بعده حتى تجعل المستخدم يتصل بالسيرفر.

دوال الكلاس Socket

الجدول التالي يحتوي على بعض دوال الكلاس Socket.

الدالة مع تعريفها
public void connect(SocketAddress host, int timeout) تحتاج هذه الدالة فقط في حال أنشأت كائن الـ Socket بواسطة الكونستركتور الذي لا يأخذ أي Argument لتبدأ الإتصال مع السيرفر.
public InetAddress getInetAddress() ترجع كائن نوعه InetAddress يمثل عنوان السيرفر الذي هو على اتصال مع البرنامج من خلال كائن الـ Socket.
ترجع null في حال كائن كائن الـ Socket غير متصل بالسيرفر.
public int getPort() ترجع رقم البورت الذي يستخدمه كائن الـ Socket في الكمبيوتر الآخر للتحكم به عن بعد.
ترجع 0 في حال كان كائن الـ Socket مغلق أو لم يبدأ عملية الإتصال بعد.
public int getLocalPort() ترجع رقم البورت الذي يستخدمه كائن الـ Socket الذي قام باستدعائها للإتصال مع السيرفر.
ترجع 1- في حال كان كائن الـ Socket مغلق أو لم يبدأ عملية الإتصال بعد.
public SocketAddress getRemoteSocketAddress() ترجع كائن نوعه SocketAddress يمثل عنوان الكمبيوتر الذي نتحكم به عن بعد.
ترجع null في حال كائن كائن الـ Socket غير متصل بالسيرفر.
public InputStream getInputStream() ترجع كائن نوعه InputStream يستخدم للقراءة من الـ Socket.
ترمي الإستثناء IOException في حال حدث خطأ عند نقل البيانات.
public OutputStream getOutputStream() ترجع كائن نوعه OutputStream يستخدم للكتابة في الـ Socket.
ترمي الإستثناء IOException في حال حدث خطأ عند نقل البيانات.
public void close() تغلق الإتصال مع السيرفر, و عندها لن يعود باستطاعتك إستخدام نفس كائن الـ Socket للإتصال بالسيرفر من جديد.

في حال حدث خطأ عند التعامل مع الـ Socket يتم رمي الإستثناء IOException
و في حال كائن الـ Socket الذي قام باستدعاءها معلق ترمي الخطأ SocketException.

الكلاس ServerSocket

الكلاس ServerSocket يستخدم لفتح بورت في السيرفر و تلقي أي إتصال من قبل أي مستخدم عادي.


بناؤه

                  public class ServerSocket
	extends Object
	implements Closeable
  


كونستركتورات الكلاس ServerSocket

الجدول التالي يحتوي على جميع الكونستركتورات الموجودة في الكلاس ServerSocket.

الكونستركتور مع تعريفه
public ServerSocket() ينشئ كائن نوعه ServerSocket ليس جاهزاً بعد لتلقي أي إتصال.
هنا ستضطر أن تستدعي الدالة accept() بعده حتى تجعل العملاء قادرين على الإتصال بالسيرفر.
public ServerSocket(int port) ينشئ كائن نوعه ServerSocket جاهز لتلقي إتصال من أي عميل على البورت الذي نمرره له كـ Argument.
في حال قمت بتمرير القيمة 0 كـ Argument فهذا يعني أن السيرفر سيختار أي رقم بورت متوفر عنده.
public ServerSocket(int port, int backlog) ينشئ كائن نوعه ServerSocket جاهز لتلقي إتصال مع عدد محدد من العملاء في نفس الوقت و على بورت محدد.

باراميترات
  • port هو رقم البورت الذي سيتم تلقي إتصلات العملاء من خلاله.

  • backlog هو رقم يمثل عدد العملاء الأقصى الذي يمكنهم الإتصال مع السيرفر في نفس الوقت.

public ServerSocket(int port, int backlog, InetAddress bindAddress) ينشئ كائن نوعه ServerSocket جاهز لتلقي إتصال مع عدد محدد من العملاء في نفس الوقت و على بورت محدد و على عنوان واحد.
إذاً يستخدم هذا الكونستركتور في حال كان السيرفر يملك أكثر من عنوان, لكنك تريد الإتصال مع العملاء من عنوان واحد.

باراميترات
  • port هو رقم البورت الذي سيتم تلقي إتصلات العملاء من خلاله.

  • backlog هو رقم يمثل عدد العملاء الأقصى الذي يمكنهم الإتصال مع السيرفر في نفس الوقت.

  • bindAddress هو كائن نوعه InetAddress يمثل عنوان السيرفر المخصص للتواصل مع العملاء.



دوال الكلاس Socket

الجدول التالي يحتوي على بعض دوال الكلاس Socket.

الدالة مع تعريفها
public Socket accept() تحتاج هذه الدالة فقط في حال أنشأت كائن الـ ServerSocket بواسطة الكونستركتور الذي لا يأخذ أي Argument لتجعل السيرفر يقبل الإتصال مع العملاء.

ترمي الإستثناء SocketException في حال حدث خطأ سببه البروتوكول المستخدم في عملية الإتصال.
ترمي الإستثناء SocketTimeoutException في حال قمنا باستدعائها قبل استدعاء الدالة accept().
ترمي الإستثناء IllegalArgumentException في حال وضعنا قيمة الباراميتر size أصغر أو تساوي صفر.
public void bind(SocketAddress endpoint) في حال أنشأت كائن الـ ServerSocket بواسطة الكونستركتور الذي لا يأخذ أي Argument, و كان السيرفر يملك أكثر من عنوان, يمكنك استدعاء هذه الدالة قبل استدعاء accept(), لتحديد العنوان الذي سيتلقى إتصالات العملاء.

مكان الباراميتر endpoint عليك تمرير كائن من إحدى الكلاسات التي ترث من الكلاس SocketAddress.
في حال قمت بتمرير القيمة null مكانه سيتلقى السيرفر إتصالات العملاء على أي عنوان يملكه - في حال كان يملك أكثر من عنوان - و على أي بورت متوفر.

ترمي الإستثناء IOException في حال فشلت عملية الإتصال أو في حال كان الإتصال مفتوح أصلاً.
ترمي الإستثناء IllegalArgumentException في حال كان الكائن الذي قمنا بتمريره لها لا يتناسب مع البروتوكول المستخدم من قبل كائن الـ ServerSocket الذي قام باستدعائها.
public void bind(SocketAddress endpoint, int backlog) في حال أنشأت كائن الـ ServerSocket بواسطة الكونستركتور الذي لا يأخذ أي Argument, و كان السيرفر يملك أكثر من عنوان, يمكنك استدعاء هذه الدالة قبل استدعاء accept(), لتحديد العنوان الذي سيتلقى إتصالات العملاء.

باراميترات
  • endpoint هو كائن من إحدى الكلاسات التي ترث من الكلاس SocketAddress يمثل عنوان السيرفر الذي سيتلقى إتصال العملاء.
    في حال قمت بتمرير القيمة null مكانه سيتلقى السيرفر إتصالات العملاء على أي عنوان يملكه - في حال كان يملك أكثر من عنوان - و على أي بورت متوفر.

  • backlog هو رقم يمثل عدد العملاء الأقصى الذي يمكنهم الإتصال مع السيرفر في نفس الوقت.


ترمي الإستثناء IOException في حال فشلت عملية الإتصال أو في حال كان الإتصال مفتوح أصلاً.
ترمي الإستثناء IllegalArgumentException في حال كان الكائن الذي قمنا بتمريره لها لا يتناسب مع البروتوكول المستخدم من قبل كائن الـ ServerSocket الذي قام باستدعائها.
public boolean isBound() ترجع true في حال كان كائن الـ ServerSocket جاهزاً لتلقي إتصالات العملاء على أي عنوان يملكه.
public void setReceiveBufferSize(int size) في حال قمت بإنشاء كائن الـ ServerSocket باستخدام الكونستركتور الذي لا يأخذ أي Argument, يمكنك إستدعاءها قبل استدعاء الدالة bind() لتحديد حجم البيانات الأقصى الذي يمكن تبادله بين الخادم و العميل.

ترمي الإستثناء SocketException في حال حدث خطأ سببه البروتوكول المستخدم في عملية الإتصال.
ترمي الإستثناء IllegalArgumentException في حال وضعت قيمة الباراميتر size أصغر أو تساوي صفر.
public InetAddress getInetAddress() ترجع كائن نوعه InetAddress يمثل عنوان كائن الـ ServerSocket.
ترجع null في حال كان كائن الـ ServerSocket غير جاهز للإتصال مع العملاء, أو في حال تم إغلاقه.
public SocketAddress getLocalSocketAddress() ترجع كائن نوعه InetAddress يمثل عنوان كائن الـ ServerSocket بالإضافة إلى رقم البورت.
ترجع null في حال كان كائن الـ ServerSocket غير جاهز للإتصال مع العملاء, أو في حال تم إغلاقه.
public int getLocalPort() ترجع رقم البورت الذي يستخدمه كائن الـ ServerSocket لتلقي إتصالات العملاء.
ترجع 1- في حال لم يقم كائن الـ ServerSocket بتحديد البورت الذي سيستخدمه قبل استدعائها.
public int getReceiveBufferSize() ترجع رقم يمثل حجم البيانات الذي يمكن تبادله بين الخادم و العميل خلال عملية الإتصال بالـ KB.
public void close() تغلق الإتصال مع العملاء. عندها لن يعود باستطاعتك إستخدام نفس كائن الـ ServerSocketلتلقي إتصالات من العملاء.

في حال حدث خطأ عند التعامل مع الـ Socket يتم رمي الإستثناء IOException
و في حال كائن الـ Socket الذي قام باستدعاءها معلق ترمي الخطأ SocketException.
public boolean isClosed() ترجع true في حال تم إغلاق كائن الـ ServerSocket بواسطة الدالة close().
غير ذلك ترجع false.
public String toString() ترجع نص يمثل عنوان و رقم بورت كائن الـ ServerSocket.

أخطاء محتملة

بعض الأخطاء التي قد تقع عند إنشاء كائن من الكلاسات Socket و ServerSocket.

  • UnknownHostException في حال كان لم يتم معرفة الـ IP Address للـ Host Name الذي قمنا بتمريره.

  • SecurityException في حال كان البرنامج ممنوع من الإتصال بالشبكة.

  • IOException في حال حدث خطأ عند نقل البيانات.

  • IllegalArgumentException في حال كان رقم البورت الموضوع ليس بين 0 و 65535.

  • NullPointerException في حال كان عنوان الجهاز الذي يحاول البرنامج الإتصال معه يساوي null, أي غير محدد.

مثال شامل

في هذا المثال  قمنا بإنشاء برنامجين ( أي مشروعين منفصلين ), واحد للسيرفر إسمه Server و واحد لأي مستخدم عادي إسمه Client.
فكرة البرنامج بشكل عام هي جعل البرنامجين قادرين على التواصل مع بعضهما البعض.

ملاحظة: عند تجربتهما عليك تشغيل برنامج السيرفر أولاً, ثم تشغيل برنامج المستخدم العادي كما فعلنا في الفيديو الذي وضعناه مكان نتيجة التشغيل.


مثال

هذا البرنامج مصمم للسيرفر, و هو موضوع في مجلد إسمه server.

Server.java
                    // هنا قمنا باستدعاء الكلاسات التي سنستخدمها لإنشاء الإتصال
	  import java.io.BufferedReader;
	  import java.io.DataInputStream;
	  import java.io.DataOutputStream;
	  import java.io.InputStreamReader;
	  import java.net.ServerSocket;
	  import java.net.Socket;

	  public class Server {

	  public static void main(String[] args) {

	  try
	  {
	  // Socket لإستقبال أي إتصال من قبل المستخدم, أي لإستقبال أي ServerSocket هنا قمنا بإنشاء كائن من الكلاس
	  ServerSocket ss = new ServerSocket(3333);

	  // آخر Socket يمكنه تبادل الببانات مع أي كائن Socket و التي ترجع كائن نوعه accept() هنا قمنا باستدعاء الدالة
	  Socket s = ss.accept();

	  // s سنستخدمه لقراءة محتوى الكائن dis الكائن
	  DataInputStream dis = new DataInputStream(s.getInputStream());

	  // s سنستخدمه للكتابة في الكائن dos الكائن
	  DataOutputStream dos = new DataOutputStream(s.getOutputStream());

	  // سنستخدمه لقراءة البيانات التي سيدخلها السيرفر من لوحة المفاتيح br الكائن
	  BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

	  // serverMsg و رسالات السيرفر ستتخزن في المتغير clientMsg رسالات المستخدم ستتخزن في المتغير
	  String serverMsg,clientMsg;

	  // end هنا سيستمر التواصل بين السيرفر و المستخدم طالما أن المستخدم لم يكتب
	  do{
	  clientMsg = dis.readUTF();
	  System.out.println("client says: "+clientMsg);
	  serverMsg = br.readLine();
	  dos.writeUTF(serverMsg);
	  dos.flush();
	  }
	  while(!clientMsg.equals("end"));

	  // عند إنتهاء الإتصال قمنا بإغلاق جميع الكائنات التي استخدمناها لإنشاء الإتصال و التي لم يعد لها حاجة
	  dis.close();
	  s.close();
	  ss.close();
	  }
	  catch(Exception e) {
	  System.out.println(e.getMessage());
	  }

	  }

	  }
	

هذا البرنامج مصمم لأي مستخدم, و هو موضوع في مجلد إسمه client.

Client.java
                    // هنا قمنا باستدعاء الكلاسات التي سنستخدمها لإنشاء الإتصال
	  import java.io.BufferedReader;
	  import java.io.DataInputStream;
	  import java.io.DataOutputStream;
	  import java.io.InputStreamReader;
	  import java.net.Socket;

	  public class Client {

	  public static void main(String[] args) {

	  try
	  {
	  // 3333 للتواصل مع برنامج آخر موجود على سيرفر و يستخدم البورت Socket هنا قمنا بإنشاء كائن من الكلاس
	  Socket s = new Socket("localhost",3333);

	  // s سنستخدمه لقراءة محتوى الكائن dis الكائن
	  DataInputStream dis = new DataInputStream(s.getInputStream());

	  // s سنستخدمه للكتابة في الكائن dos الكائن
	  DataOutputStream dos = new DataOutputStream(s.getOutputStream());

	  // سنستخدمه لقراءة البيانات التي سيدخلها المستخدم من لوحة المفاتيح br الكائن
	  BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

	  // serverMsg و رسالات السيرفر ستتخزن في المتغير clientMsg رسالات المستخدم ستتخزن في المتغير
	  String serverMsg, clientMsg;

	  // end هنا سيستمر التواصل بين السيرفر و المستخدم طالما أن المستخدم لم يكتب
	  do{
	  clientMsg = br.readLine();
	  dos.writeUTF(clientMsg);
	  dos.flush();
	  serverMsg = dis.readUTF();
	  System.out.println("server says: "+serverMsg);
	  }
	  while(!clientMsg.equals("end"));

	  // عند إنتهاء الإتصال قمنا بإغلاق جميع الكائنات التي استخدمناها لإنشاء الإتصال و التي لم يعد لها حاجة
	  dis.close();
	  dos.close();
	  s.close();
	  }
	  catch(Exception e) {
	  System.out.println(e.getMessage());
	  }

	  }

	  }
	

ستحصل على نتيجة تشبه النتيجة التالية عند التشغيل.