آموزش جاوا – قسمت 13 (Networking)

در جاوا مفهوم networking به ارتباط دو یا چند device گفته‌ می‌شود که از طریق یک شبکه به یکدیگر متصلند. با شنیدن network programming در جاوا، اولین چیزی که باید به ذهنتان برسد، کلاینت و سرور است، چیزی که در برخی مدل‌سازی‌های شبکه‌های SDN و در بخش‌های مختلف آن مورد استفاده قرار گرفته است. بنابراین تصمیم گرفتیم تا این قسمت از آموزش جاوا را به این مفهوم اختصاص دهیم. پس تا انتهای آموزش با ما همراه باشید…

پیش از اینکه به اصل مطلب بپردازیم شاید بد نباشد تا از مزایای networking در جاوا صحبت کنیم. استفاده از این مفهوم در جاوا دو کاربرد و مزیت مهم دارد:

  • اشتراک منابع
  • مدیریت مرکزی نرم‌افزار

در جاوا کلاس‌ها و interfaceهای مختلفی برای networking اختصاص داده شده است که در ادامه به توضیح آن‌ها خواهیم پرداخت. خبر خوب اینکه در هر یک از آن‌ها جزئیات ارتباطی در سطح خیلی پایین تعریف شده است تا بتوانید برنامه‌هایی بنویسید که تمرکزشان روی همان مسئله خاص شما باشد. لازم به ذکر است که تمام کلاس‌های مربوط به این مفهوم در packageای از جاوا به نام java.net قرار دارند. که این پکیج از دو پروتکل رایج شبکه پشتیبانی می‌کند:

  • TCP: همانطور که می دانید مشخصه بارز این پروتکل ایجاد یک ارتباط قابل اطمینان بین دو برنامه کاربردی است.
  • UDP: از گذشته‌های دور این پروتکل در مقابل پروتکل TCP  به connection-less بودنش معروف بوده و … همچنان هست!

یادآوری این دو مشخصه برای پروتکل‌های TCP و UDP را  از این جهت ضروری دانستیم که شما باید بتوانید با توجه به کاربرد مورد نیاز خود، نوع ارتباط بین دو اپلیکیشن را تشخیص داده و  از یکی از این دو پروتکل در برنامه‌نویسی خود استفاده کنید.

در ادامه این آموزش شما با مفهوم Socket Programming  آشنا خواهید شد. مفهوم Socket programming یکی از مفاهیم شبکه‌ای رایج در برنامه‌نویسی جاوا است. بنابراین سعی می‌کنیم تا با جزئیات بیشتری این مفهوم را توضیح دهیم.

Socket Programming:

از این مفهوم برای ایجاد ارتباط بین اپلیکیشن‌هایی استفاده می‌شود که در JREهای مختلف قرار دارند. socket programming می‌تواند به صورت connection oriented یا connection less انجام شود. از کلاس‌های Socket و ServerSocket برای حالت connection oriented استفاده شده و از کلاس‌های DatagramSocket و DatagramPacket برای برنامه‌نویسی در حالت connection-less استفاده می‌شود. در اینجا به connection-oriented یعنی استفاده از پروتکل TCP خواهیم پرداخت.

سوکت‌ها وظیفه دارند تا با استفاده از پروتکل TCP یک مکانیسم ارتباطی بین دو کامپیوتر ایجاد کنند. در این ارتباط کلاینت یک سوکت در سمت خود ایجاد کرده و تلاش می‌کند تا به سوکت سمت سرور متصل شود. با ایجاد این ارتباط، سرور در سمت خود یک object از سوکت ایجاد می‌کند. حال از این مرحله به بعد کلاینت و سرور  خواهند توانست با نوشتن و خواندن از سوکت با یکدیگر ارتباط برقرار کنند.

همانطور که در بالا نیز اشاره شد کلاس java.net.Socket نمایانگر یک socket است و کلاس java.net.ServerSocket فراهم‌کننده یک مکانیسم برای سمت سرور است تا بتواند به کلاینت گوش کرده و با آن ارتباط برقرار کند.

برای درک بهتر این موضوع بهتر است تا مراحلی که هنگام ایجاد ارتباط TCP بین دو کامپیوتر با استفاده از سوکت اتفاق می‌افتد را دنبال کنیم:

  1. ایجاد یک instance از شی ServerSocket توسط سرور که در آن باید پورت ارتباطی که قرار است ایجاد شود مشخص شود.
  2. فراخوانی تابع ()accept از کلاس ServerSocket توسط سرور. (این تابع تا زمانی که کلاینت به پورت تعیین شده در مرحله قبل  متصل شود، در حالت انتظار باقی می‌ماند)
  3. ایجاد یک شی از Socket توسط کلاینت با مشخص کردن نام سرور و شما پورت متصل به آن.
  4. حال constructor کلاس Socket سعی خواهد کرد تا کلاینت را با شماره پورت مشخص شده به سرور متصل کند. در صورت برقراری موفقیت‌آمیز این اتصال، کلاینت یکی شی Socket خواهد داشت که قادر خواهد بود تا با سرور ارتباط برقرار کند.
  5. تابع ()accept یک reference به سوکت جدید سمت سرور که به کلاینت متصل شده است بازخواهد گرداند.

پس از ایجاد اتصال، ارتباط از طریق I/O streamها قابل برقراری خواهد بود. هر سوکت خواهد توانست از هر دوی OutputStream و InputStream استفاده کند. به این صورت که OutputStream  کلاینت یا سرور به InputStream دیگری متصل شده و برعکس.

لازم به ذکر است از آنجایی که TCP یک پروتکل ارتباطی دو طرفه است، داده می‌تواند همزمان از هر دو stream ارسال شود.

کلاس‌های Socket و ServerSocket توابع زیادی دارند که در ادامه به شرح مهم‌ترین توابع این دو کلاس خواهیم پرداخت…

توابع مهم کلاس Socket:

  1. ()public InputStream getInputStream: این تابع همانطور که از نامش پیداست، input stream سوکت را برمی‌گرداند
  2. ()public OutputStream getOutputStream: مشابه تابع بالا، در این تابع output stream بازگردانده می‌شود.
  3. ()public synchronized void close: با اجرای این تابع، سوکت بسته می‌شود. بنابراین دیگر شی Socket مربوطه قادر به  اتصال مجدد به هیچ سروری نخواهد بود.

توابع مهم کلاس ServerSocket:

  1. (public void setSoTimeout(int timeout: با این تابع می‌توان مشخص کرد که نیاز داریم سرور حداکثر چه مدت زمانی حین اجرای تابع accept منتظر یک کلاینت بماند. پس از آشنایی با تابع بعدی بیشتر متوجه اهمیت این تابع خواهید شد.
  2. public Socket accept:همانطور که در اوایل آموزش این قسمت توضیح داده شد، با فراخوانی این تابع، سرور منتظر کلاینت می‌ماند. مادامی که هنوز هیچ کلاینتی به پورت مربوطه در سرور متصل نشده است این تابع بلاک می‌شود (مگر اینکه سوکت time out شود). بنابراین می‌توان نتیجه گرفت که در صورت مشخص نکردن زمان time out توسط تابع ()setSoTimeout ممکن است این تابع مدت زمان نامحدودی در حالت بلاک باقی بماند.
  3. public synchronized void close: با اجرای این تابع سوکت سمت سرور بسته می‌شود.

برای درک بهتر این مفاهیم و تئوری‌های گفته شده، بهتر است تا با یک مثال عملی  از socket programming آشنا شویم:

Socket Client:

کلاس GreetinClient تعریف شده در مثال زیر یک کلاینت است که با استفاده از یک سوکت به سرور متصل شده و پیغامی را به آن ارسال کرده و منتظر پاسخ باقی می‌ماند:

public class GreetingClient {

   public static void main(String [] args) {
      String serverName = args[0];
      int port = Integer.parseInt(args[1]);
      try {
         System.out.println("Connecting to " + serverName + " on port " + port);
         Socket client = new Socket(serverName, port); //ایجاد یک سوکت برای اتصال به پورتی مشخص از یک سرور خاص
         
         System.out.println("Just connected to " + client.getRemoteSocketAddress());
         OutputStream outToServer = client.getOutputStream();
         DataOutputStream out = new DataOutputStream(outToServer);
         
         out.writeUTF("Hello from " + client.getLocalSocketAddress()); //برقراری ارتباط با سرور
         InputStream inFromServer = client.getInputStream();
         DataInputStream in = new DataInputStream(inFromServer);
         
         System.out.println("Server says " + in.readUTF());
         client.close();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

 Socket Server:

در مثال زیر یک سرور را مشاهده می‌کنید که با استفاده از کلاس Socket روی پورت مشخصی که از طریق command line توسط کاربر تعیین شده است، گوش داده و منتظر اتصال یک کلاینت می‌ماند:

*درباره مفهوم thread در جلسه آینده صحبت خواهیم کرد.

public class GreetingServer extends Thread {
   private ServerSocket serverSocket;
   
   public GreetingServer(int port) throws IOException {
      serverSocket = new ServerSocket(port);
      serverSocket.setSoTimeout(10000);
   }

   public void run() {
      while(true) {
         try {
            System.out.println("Waiting for client on port " + 
               serverSocket.getLocalPort() + "..."); //شماره پورتی که سرور روی آن گوش می‌دهد
            Socket server = serverSocket.accept();
            
            System.out.println("Just connected to " + server.getRemoteSocketAddress());
            DataInputStream in = new DataInputStream(server.getInputStream());
            
            System.out.println(in.readUTF());
            DataOutputStream out = new DataOutputStream(server.getOutputStream());
            out.writeUTF("Thank you for connecting to " + server.getLocalSocketAddress()
               + "\nGoodbye!");
            server.close();
            
         } catch (SocketTimeoutException s) {
            System.out.println("Socket timed out!");
            break;
         } catch (IOException e) {
            e.printStackTrace();
            break;
         }
      }
   }
   
   public static void main(String [] args) {
      int port = Integer.parseInt(args[0]);
      try {
         Thread t = new GreetingServer(port);
         t.start();
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

بهتر است تا با یک IDE آنلاین مانند tutorialspoint مثال‌ بالا را خودتان اجرا و بررسی کنید. پس از کامپایل کلاینت و سرور، سرور به این صورت اجرا خواهد شد:

$ java GreetingServer 6066
Waiting for client on port 6066...

با اجرا شدن کلاینت، خروجی زیر را مشاهده خواهید کرد:

$ java GreetingClient localhost 6066
Connecting to localhost on port 6066
Just connected to localhost/127.0.0.1:6066
Server says Thank you for connecting to /127.0.0.1:6066
Goodbye!

 

آموزش این قسمت از جاوا نیز به پایان رسید. از این پس شما خواهید توانست تا از مفهوم برنامه‌نویسی سوکت، در مدل‌سازی‌های کلاینت-سروری در شبکه‌های SDN استفاده کنید. در صورت هرگونه سوال از این مبحث می‌توانید سوالات خود را از طریق آی دی تلگرام @SM_SDN مطرح کنید. بدرود تا قسمت بعدی آموزش جاوا که درباره مفهوم Thread مطالبی را خدمت شما همراهان عزیز ارائه خواهیم داد…

 

تاریخچه آموزش جاوا به ترتیب:

قسمت اول آموزش جاوا (مقدمه)

قسمت دوم آموزش جاوا (Types and Variables)

قسمت سوم آموزش جاوا (دستورات شرطی)

قسمت چهارم آموزش جاوا (آرایه‌ها)

قسمت پنجم آموزش جاوا (حلقه‌ها)

قسمت ششم آموزش جاوا (توابع)

قسمت هفتم آموزش جاوا (اشیا)

قسمت هشتم آموزش جاوا (ارث‌بری)

قسمت نهم آموزش جاوا (try and catch)

قسمت دهم آموزش جاوا (کلاس‌های abstract)

قسمت یازدهم آموزش جاوا (Interfaces)

قسمت دوازدهم آموزش جاوا (مفهوم Generic)

قسمت سیزدهم آموزش جاوا (Networking)

قسمت چهاردهم آموزش جاوا (مفهوم نخ و رشته)

آموزش جاوا – قسمت 13 (Networking)
امتیاز دهید

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *