Programming Basics SQL HTML CSS JavaScript React Python C++ Java JavaFX Swing Problem Solving English English Conversations Computer Fundamentals Linux Learn Typing

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

  • الكلاس 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.

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

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

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

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

باراميترات
  • address هو كائن نوعه InetAddress يمثل عنوان السيرفر الذي سنحاول الإتصال به من برنامج المستخدم.
  • port هو رقم يمثل البورت المحجوز في السيرفر خصيصاً للبرنامج الذي سنحاول الإتصال به من برنامج المستخدم.
  • localAddr هو كائن نوعه InetAddress يمثل عنوان كمبيوتر المستخدم الذي يحاول الإتصال بالسيرفر.
  • localPort هو رقم يمثل البورت الذي سيتم حجزه خصيصاً للبرنامج في جهاز المستخدم.
5 public Socket() ينشئ كائن نوعه Socket لكنه لا يجعل برنامج المستخدم متصلاً مع السيرفر.
هنا ستضطر أن تستدعي الدالة connect() بعده حتى تجعل المستخدم يتصل بالسيرفر.

دوال الكلاس Socket

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

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

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

الكلاس ServerSocket

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


بناؤه

public class ServerSocket
extends Object
implements Closeable


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

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

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

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

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


دوال الكلاس Socket

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

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

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

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

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

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

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

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

في حال حدث خطأ عند التعامل مع الـ Socket يتم رمي الإستثناء IOException
و في حال كائن الـ Socket الذي قام باستدعاءها معلق ترمي الخطأ SocketException.
11 public boolean isClosed() ترجع true في حال تم إغلاق كائن الـ ServerSocket بواسطة الدالة close().
غير ذلك ترجع false.
12 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());
        }
 
    }
 
}

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

متصفحك لا يدعم صيغة الفيديو!