แสดงบทความที่มีป้ายกำกับ Distributed Computing แสดงบทความทั้งหมด
แสดงบทความที่มีป้ายกำกับ Distributed Computing แสดงบทความทั้งหมด

วันศุกร์ที่ 1 กุมภาพันธ์ พ.ศ. 2562

การเขียนโปรแกรมเครือข่ายด้วยซ็อกเก็ต (Socket) ตอนที่ 9: UDP Client ด้วยภาษาจาวา (Java)

หลังจากสร้างโปรแกรมเซิร์ฟเวอร์ไปแล้วจากตอนที่ 8 ตอนนี้มาดูโปรแกรมฝั่งไคลเอนต์กันครับ มาดูโค้ดกันเลยครับ


  1. import java.io.*; 
  2. import java.net.*;
  3. import java.util.*; 
  4. class UDPClient { 
  5.    public static void main(String args[]) throws Exception { 
  6.       Scanner inFromUser = new Scanner(System.in);
  7.       DatagramSocket clientSocket = new DatagramSocket(); 
  8.       InetAddress IPAddress = InetAddress.getByName("localhost"); 
  9.       byte[] sendData = new byte[1024]; 
  10.       byte[] receiveData = new byte[1024]; 
  11.       System.out.print("Please enter words: ");
  12.       String sentence = inFromUser.nextLine();
  13.       sendData = sentence.getBytes();  
  14.       DatagramPacket sendPacket = 
  15.          new DatagramPacket(sendData, sendData.length, IPAddress, 9876); 
  16.       clientSocket.send(sendPacket); 
  17.       DatagramPacket receivePacket = 
  18.          new DatagramPacket(receiveData, receiveData.length); 
  19.       clientSocket.receive(receivePacket); 
  20.       String modifiedSentence = new String(receivePacket.getData()); 
  21.       System.out.println("FROM SERVER:" + modifiedSentence.trim()); 
  22.    clientSocket.close(); 
  23.    } 

บรรทัดที่ 7 สร้างซ็อกเก็ตเพื่อใช้ในการแลกเปลี่ยนข้อมูลกับเซิร์ฟเวอร์ บรรทัดที่ 8 แปลงชื่อโดเมนของเซิร์ฟเวอร์เป็นหมายเลขไอพี ในที่นี้เรารันโปรแกรมไคลเอนต์และเซิร์ฟเวอร์บนเครื่องเดียวกันชื่อโดเมนจึงเป็น localhost บรรทัดที่ 14-15 เตรียมแพ็กเก็ตที่ใช้ส่งข้อมูล โดยนอกจากข้อมูลที่จะส่งไปแล้ว ก็ต้องระบุหมายเลขไอพี และหมายเลขพอร์ตของโปรแกรมเซิร์ฟเวอร์ และถึงแม้เราจะไม่ได้ระบุหมายเลขไอพี และหมายเลขพอร์ตของไคลเอนต์ที่เป็นผู้ส่ง ข้อมูลนี้จะถูกใส่ลงไปในแพ็กเก็ตโดยอัตโนมัติ บรรทัดที่ 19 ส่งข้อมูลผ่านซ็อกเก็ตไปให้เซิร์ฟเวอร์ บรรทัดที่ 20 รอรับข้อมูลที่เซิร์ฟเวอร์จะตอบกลับมา

โดยโค้ดโปรแกรมสามารถดาวน์โหลดได้จาก github ของผมครับ ไส่วนใตรที่โหลดไปแล้วตั้งแต่เรื่องของฝั่งเซิร์ฟเวอร์ ก็ไม่ต้องโหลดซ้ำแล้วนะครับ 

การเขียนโปรแกรมเครือข่ายด้วยซ็อกเก็ต (Socket) ตอนที่ 8: UDP Server ด้วยภาษาจาวา (Java)

จากตอนที่ 7  ก็คงเข้าใจความแตกต่างระหว่างทีซีพี (TCP) กับยูดีพี (UDP) กันแล้วนะครับ คราวนี้เราจะมาเขียนโปรแกรมฝั่งเซิร์ฟเวอร์กัน โดยโปรแกรมเซิร์ฟเวอร์นี้จะทำงานเหมือน TCPServer คือรับข้อมูลสตริงจากไคลเอนต์ เอามาแปลงเป็นตัวอักษรตัวใหญ่ แล้วส่งกลับไปให้ไคลเอนต์ มาดูโค้ดกันเลยครับ


  1. import java.io.*; 
  2. import java.net.*; 
  3. class UDPServer { 
  4.   public static void main(String args[]) throws Exception 
  5.     { 
  6.     DatagramSocket serverSocket = new DatagramSocket(9876); 
  7.       byte[] receiveData = new byte[1024]; 
  8.       byte[] sendData  = new byte[1024];
  9.       while(true) 
  10.       { 
  11.             System.out.println("The server is waiting ");
  12.             DatagramPacket receivePacket = 
  13.              new DatagramPacket(receiveData, receiveData.length); 
  14.             serverSocket.receive(receivePacket);
  15.             String sentence = new String(receivePacket.getData());
  16.             InetAddress IPAddress = receivePacket.getAddress(); 
  17.             int port = receivePacket.getPort(); 
  18.             String capitalizedSentence = sentence.toUpperCase();
  19.             sendData = capitalizedSentence.getBytes(); 
  20.             DatagramPacket sendPacket = 
  21.                   new DatagramPacket(sendData, sendData.length, IPAddress, port); 
  22.               serverSocket.send(sendPacket); 
  23.         } 
  24.     } 
  25. }  
การสร้างซ็อกเก็ตจะใช้คลาส DatagramSocket ตามที่เขียนไว้ในบรรทัดที่ 6 ในที่นี้โปรแกรมเซิร์ฟเวอร์จะรอรับการติดต่ออยู่ที่พอร์ตหมายเลข 9876  บรรทัดที่ึ 7 และ 8 จะสร้างบัฟเฟอร์สำหรับรับและส่งข้อมูลตามลำดับ โดยในโปรแกรมนี้ระบุขนาดของการรับส่งข้อมูลไว้ที่ไม่เกิน 1024 ไบต์ บรรทัดที่ 12-13 สร้างแพ็กเก็ตรับข้อมูล โดยระบุว่าให้เอาข้อมูลมาเก็บไว้ในบัฟเฟอร์คือ receiveData ด้วยความยาวตามขนาดของบัฟเฟอร์ที่ประกาศไว้ ในตัวอย่างนี้คือ 1024 ไบต์ บรรทัดที่ 4 รออ่านข้อมูลที่ไคลเอนต์จะส่งมา โดยข้อมูลจะถูกเก็บลงแพ็กเก็ตที่ประกาศไว้ในบรรทัดที่ 12 

บรรทัดที่ 15-17 ดึงข้อมูลออกมาจากแพ็กเก็ต โดยบรรทัดที่ 15 เป็นการดึงตัวข้อมูลคือ บรรทัดที่ 16 และ 17 ดึงไอพีแอสเดรสและหมายเลขพอร์ตของไคลเอนต์ที่ส่งข้อมูลมาตามลำดับ สองบรรทัดนี้มีความจำเป็นเพราะการใช้โปรโตคอลยูดีพีไม่มีการสร้างการติดต่อไว้ก่อน ข้อมูลของผู้ส่งอย่างไอพีแอสเดรสและหมายเลขพอร์ตจะถูกส่งมาเป็นส่วนหนึ่งของแพกเก็ตด้วย

บรรทัดที่ 20 สร้างแพ็กเก็ตสำหรับส่งข้อมูลกลับ โดยระบุว่าจะส่งกลับไปที่ไหนโดยใช้ค่าหมายเลขไอพี และหมายเลขพอร์ตที่ดึงมาจากบรรทัดที่ 16 และ 17 ข้อสังเกตก็คือถึงแม้เราจะไม่ได้ระบุหมายเลขไอพีและหมายเลขพอร์ตของเซิร์ฟเวอร์ ซึ่งในจุดนี้คือผู้ส่ง แต่ข้อมูลทั้งสองนี้จะถูกใส่เข้าไปในแพ็กเก็ตโดยอัตโนมัติ บรรทัดที่ 22 เป็นการส่งแพ็กเก็ตผ่านซ็อกเก็ตกลับไปให้ไคลเอนต์ 

สำหรับโค้ดของโปรแกรมนี้สามารถดาวน์โหลดได้ที่ github ของผมครับ ซึ่งจะได้ทั้งโปรแกรมเซิร์ฟเวอร์และไคลเอนต์ ส่วนของไคลเอต์จะเขียนถึงในบล็อกถัดไปครับ

วันพฤหัสบดีที่ 30 เมษายน พ.ศ. 2558

Java RMI ตอนที่ 3: การเขียนโปรแกรมฝั่งไคลเอนต์และการรันโปรแกรม

มาถึงตอนสุดท้ายซะที ส่วนใครที่ยังไม่ได้อ่านตอน 1 และตอน 2 ก็เชิญไปอ่านกันก่อนนะครับ ในตอนนี้เราจะมาเขียนโปรแกรมฝั่งไคลเอนต์และลองรันโปรแกรมกันครับ มาดูโปรแกรมฝั่งไคลเอนต์กันก่อนครับ

 1 import java.rmi.*;
 2 import java.net.*;
 3 import java.rmi.registry.LocateRegistry;
 4 import java.rmi.registry.Registry;
 5
 6
public class MyMathClient
 7 {
 8    public static void main(String[] args)
 9    {
10       if (args.length == 0)
11       {
12          System.err.println("Usage java MyMathClient <server-hostname>");
13       }
14       try
15       {
16          Registry registry = LocateRegistry.getRegistry(args[0]);
17          MyMath stub = (MyMath) registry.lookup("MyMathServer");
18          System.out.println(stub.add(3,2));

19         
20       }
21       catch (Exception e)
22       {
23          System.err.println("System Exception" + e);
24       }
25    }
26 }

จากโปรแกรมในบรรทัดที่ 16  เป็นการค้นหาอาร์เอ็มไอรีจิสตรี (RMI registry) ในเครื่องที่อ็อบเจกต์เซิร์ฟเวอร์รันอยู่ เราจะรันโปรแกรมนี้โดยระบุชื่อเครื่องเซิร์ฟเวอร์มาเป็นพารามิเตอร์บรรทัดคำสั่ง (command line parameter) และส่งชื่อเครื่องเซิร์ฟเวอร์นี้ให้กับเมท็อด getRegistry() บรรทัดที่ 17 ค้นหาอ็อบเจกต์โดยระบุชื่ออ็อบเจกต์เป็นชื่อเดียวกันกับที่ฝั่งเซิร์ฟเวอร์ลงทะเบียนไว้ ในที่นี้คือ MyMathServer ซึ่งที่เราจะได้กลับมาจากการเรียกเมท็อด lookup() ในบรรทัดนี้ก็คืออ็อบเจ็กต์เรฟเฟอร์เรนซ์ (object reference) ไปยังอ็อบเจกต์บนฝั่งเซิร์ฟเวอร์ ให้สังเกตว่าเราใช้ชื่อของอินเทอร์เฟซ (interface) คือ MyMath ไม่ใช่ชื่อของคลาสผู้รับใช้ (servant class) MyMathImpl หลังจากนี้เราก็สามารถเรียกใช้เมท็อดของอ็อบเจกต์บนฝั่งเซิร์ฟเวอร์ได้ราวกับอ็อบเจกต์นั้นอยู่บนเครื่องเดียวกับไคลเอนต์ ดังแสดงในบรรทัดที่ 18 ซึ่งเป็นการเรียกใช้เมท็อด add()

คราวนี้ก็มารันโปรแกรมกันครับ เริ่มจากรัน rmiregistry ก่อน วันนี้ขอแสดงการรันบน Mac นะครับ คำสั่งที่ใช้ก็คือ rmiregistry & เพื่อให้โปรแกรม rmiregistry ไปทำงานอยู่หลังฉาก ดังแสดงในรูปต่อไปนี้ครับ



ถ้าใช้ Windows ก็เปิดคอมมานด์พรอมพท์ของวินโดวส์แล้วก็ใช้ start rmiregistry ได้ครับ 

หลังจากนั้นก็รันโปรแกรมเซิร์ฟเวอร์ก็เหมือนการรันโปรแกรมปกติครับคือ 

java MyMathServer

ก็จะได้ผลลัพธ์ตามรูป





และจากนั้นก็รันไคลเอนต์ java MyMathClient ซึ่งก็จะได้ผลลัพธ์ตามรูปครับ 



เอาล่ะครับจบแล้ว ก็หวังว่าจะเป็นความรู้เบื้องต้นให้ทำตามแล้วนำไปต่อยอดกันต่อได้นะครับ...

สำหรับใครที่ไม่อยากป้อนโปรแกรมเองก็สามารถดาวน์โหลดได้จาก GitHub ครับ ถ้าใครโหลดไปแล้วจากตอนที่แล้วก็ไม่ต้องโหลดแล้วนะครับ 









วันพุธที่ 29 เมษายน พ.ศ. 2558

Java RMI ตอนที่ 2: การเขียนโปรแกรมฝั่งเซิร์ฟเวอร์

หลังจากเข้าใจว่าจาวาอาร์เอ็มไอ (Java RMI) คืออะไรแล้วจากตอนที่ 1 ในตอนนี้เราจะเริ่มมาพัฒนาโปรแกรมกันครับ โดยเราจะเริ่มจากฝั่งเซิร์ฟเวอร์กันก่อน โดยเราจะต้องคิดว่าอ็อบเจกต์ที่เราจะพัฒนาขึ้นจะให้บริการอะไรบ้าง โดยเราจะต้องเขียนรายการแอ็บแสตรกท์เมท็อด (abstract method) ที่จะให้บริการข้ามเครื่องในรูปแบบอินเทอร์เฟซ (interface) และไม่ใช่อินเทอร์เฟซธรรมดาจะต้องเป็นอินเทอร์เฟซระยะไกล (remote interface) ด้วย การประกาศอินเทอร์เฟซระยะไกลทำได้ง่ายมากด้วยการใช้รูปแบบการประกาศอินเทอร์เฟซของจาวาตามปกติ แต่ให้อินเทอร์เฟซนั้นเอ็กซ์เทนด์ (extend) อินเทอร์เฟซที่จาวาเตรียมไว้ให้แล้วที่ชื่อว่า Remote สรุปก็คือรูปแบบการประกาศอินเทอร์เฟซระยะไกลมีรูปแบบดังนี้

interface <interface name> extends Remote {...}

ไม่เอาดีกว่าอธิบายแบบนี้อาจไม่เข้าใจ มาดูตัวอย่างกันดีกว่า สมมติเราจะสร้างอ็อบเจกต์ที่จะให้บริการขึ้นมาสักตัวหนึ่ง เราจะสร้างอะไรกันดีครับ เอา Hello Wolrd ดีไหม... ไม่ดีนะครับน่าเบื่อ เอาอย่างนี้แล้วกันสร้างอ็อบเจกต์ที่ให้บริการบวกลบเลขดีไหมครับ น่าเบื่อน้อยลงหน่อย เอาตามนี้นะครับ มาสร้างกันเลย จากอ็อบเจกต์ที่เราต้องการสามารถสร้างอินเทอร์เฟซระยะไกลได้ดังนี้

1 import java.rmi.Remote;
2 import java.rmi.RemoteException;
3 interface MyMath extends Remote {
  public int add(int num1, int num2) throws RemoteException;
  public int subtract(int num1, int num2) throws RemoteException;
6 }


จากโค้ดตัวอย่างก็คือเราสร้างอินเทอร์เฟซระยะไกลชื่อ MyMath ซึ่งมีแอ็บสแตรกท์เมท็อดสองตัวคือ add() และ subtract() ให้สังเกตว่าแต่ละเมท็อดที่เราจะให้ถูกเรียกใช้จากระยะไกลได้จะต้องโทรว์ (throw) เอ็กซ์เซปชัน (Exception) ที่ชื่อว่า RemoteException

ตอนนี้เราจะเริ่มสร้างคลาสกันครับ ซึ่งคลาสที่เราจะสร้างบนฝั่งเซิร์ฟเวอร์นี้เรามักจะสร้างแบ่งเป็นสองคลาสครับ คือคลาสที่เรียกว่าคลาสผู้รับใช้ (servant class) และคลาสผู้บริการ (server class) คลาสผู้รับใช้เป็นคลาสที่เราอิมพลีเมนต์อินเทอร์เฟซ หรือพูดง่าย ๆ ก็คือมันเป็นคลาสที่สร้างอ็อบเจกต์ที่จะถูกเรียกใช้จากไคลเอนต์ นี่คือเหตุผลที่เราเรียกมันว่าคลาสผู้รับใช้ นอกจากนี้สตับและสเกเลตันจะถูกสร้างจากคลาสนี้เช่นกัน ส่วนคลาสผู้บริการจะเป็นคลาสที่ใช้ในการลงทะเบียนคลาสผู้รับใช้เข้ากับอาร์เอ็มไอรีจีสตรี (RMI Registry) เอาละครับมาเขียนคลาสผู้รับใช้กันดีกว่า

 1 import java.rmi.RemoteException;
 2 import java.rmi.server.UnicastRemoteObject;
 3 public class MyMathImpl extends UnicastRemoteObject implements MyMath  {
 4    public MyMathImpl() throws RemoteException {
 5      super();
 6    }
 7    public int add(int num1, int num2) throws RemoteException  {
 8       return num1+num2;
 9    }
10    public int subtract(int num1, int num2)throws RemoteException {
11       return num1-num2;
12    }
13
14
}

ข้อสังเกตสำหรับคลาสนี้มีสามเรื่องครับ เรื่องแรกก็คือชื่อของคลาส (บรรทัดที่ 3) นักเขียนโปรแกรมที่พัฒนาจาวาอาร์เอ็มไอนิยมตั้งกันคือคลาสจะมีชื่อเดียวกับอินเทอร์เฟซตามด้วย Impl จากโปรแกรมที่เรากำลังพัฒนาอินเทอร์เฟซของเรามีชื่อว่า MyMath ดังนั้นเราจึงตั้งชื่อคลาสนี้ว่า MyMathImpl เรื่องที่สองก็คือคลาสนี้สืบทอดคุณสมบัติมาจากคลาส UnicastRemoteObject ซึ่งเป็นคลาสที่จาวาเตรียมเอาไว้เพื่อสนับสนุนการเรียกอ็อบเจกต์ข้ามเครื่อง เรื่องที่สามก็คือเราต้องมีคอนสตรักเตอร์ (constructor) ที่โทรว์ RemoteException และเรียกคอนสตรักเตอร์ของคลาส UnicastRemoteObject เอาละครับเรียบร้อยครับ เห็นไหมครับว่าง่ายมาก

[หลังจากเราเขียนคลาสผู้รับใช้เสร็จแล้วเราก็คอมไพล์เพื่อให้ได้ MyMathImpl.class ครับ ถ้าเราใช้จาวาก่อนเวอร์ชัน 5 เราก็ใช้คำสั่งนี้ในการสร้างสตับและสเกเลตันได้เลยครับ

rmic MyMathImpl

เราจะได้ไฟล์ชื่อ MyMathImpl_Stub.class ซึ่งไฟล์นี้จะทำหน้าที่เป็นทั้งสตับและสเกเลตัน 

ซึ่งผมว่าตอนนี้เราคงไม่ใช้ภาษาจาวาเก่าแบบนั้นแล้วนะครับ ดังนั้นถ้าใครใช้ Java 5  ขึ้นมาก็ข้ามไปได้เลยนะครับ เพราะในจาวารุ่นใหม่จาวารันไทม์ (Java Runtime) จะจัดการเรื่อง Stub-Skeleton ให้

]

คราวนี้มาดูคลาสผู้ให้บริการกัน

 1 import java.net.*;
 2 import java.rmi.*;
 3 import java.rmi.registry.Registry;
 4 import java.rmi.registry.LocateRegistry;
 5 public class MyMathServer 
 6 {
 7    public static void main(String[] args)
 8    {
 9      
10       try
11       {
12          MyMathImpl myMathObj = new MyMathImpl();
13          Registry registry = LocateRegistry.getRegistry();
14          registry.bind("MyMathServer", myMathObj);
15          System.out.println("MyMath Server ready");
16       }
17       catch (Exception re) {
18          System.out.println("Execption in MyMathServer: " + re);
19       }
20    }
21 }

จากโปรแกรมจะเห็นว่าเราสร้างอ็อบเจกต์เรฟเฟอร์เรนซ์ (object referennce) ของคลาส MyMathImpl ในบรรทัดที่ 12 จากนั้นบรรทัดที่ 13 ประโยค

Registry registry = LocateRegistry.getRegistry();

เป็นการค้นหาอาร์เอ็มไอรีจิสตรีที่รันอยู่ในระบบ นั่นหมายความว่าก่อนจะรันโปรแกรมเซิร์ฟเวอร์เราต้องรันอาร์เอ็มไอรีจิสตรีก่อนซึ่งจะรันยังไงนั้นจะอยู่ในตอนต่อไปครับ บรรทัดที่ 14 ประโยค

 registry.bind("MyMathServer", myMathObj);

เป็นการลงทะะเบียนอ็อบเจกต์เรฟเฟอร์เรนซ์ดังกล่าวในชื่อ MyMathServer ซึ่งโปรแกรมฝั่งไคลเอนต์ที่เราจะเขียนในตอนต่อไปจะค้นหาอ็อบเจกต์นี้โดยใช้ชื่อนี้

เอาล่ะครับตอนนี้เราก็เสร็จงานด้านเซิร์ฟเวอร์แล้ว ตอนต่อไปเราจะไปเขียนไคลเอนต์ แล้วก็ลองรันกันดูครับ โปรดอดใจสักนิดหนึ่งนะครับ ขอพักหน่อย ถ้าใครไม่อยากพิมพ์โปรแกรมเองสามารถดาวน์โหลดโค้ดได้จาก Github ครับ 


วันอังคารที่ 28 เมษายน พ.ศ. 2558

Java RMI ตอนที่1: Java RMI คืออะไร

การเรียกใช้ฟังก์ชันที่อยู่บนคอมพิวเตอร์เครื่องอื่นในปัจจุบันกลายเป็นเรื่องที่เราใช้กันแทบจะเป็นปกติในปัจจุบันกันแล้ว เช่นเราเขียนโปรแกรมเรียกใช้ฟังก์ชันของ Google Maps  เพื่อนำแผนที่มาใช้กับโปรแกรมของเราโดยเราไม่ต้องรู้เลยว่า แผนที่นั้นจัดเก็บอย่างไร ฟังก์ชันที่เราเรียกใช้เขียนขึ้นมาด้วยภาษาอะไร และโปรแกรมแผนที่ทำงานอยู่บนระบบปฏิบัติการอะไร ซึ่งเทคโนโลยีที่เราใช้เป็นพื้นฐานของการทำงานดังกล่าวในปัจจุบันก็คือเทคโนโลยีที่เรียกว่าเว็บเซอร์วิส (web service) นั่นเอง ซึ่งผมก็หวังว่าจะหาเวลามาเขียนให้ได้อ่านกันสักวันหนึ่ง 

จริง ๆ แล้วการเรียกใช้ฟังก์ชันที่ทำงานอยู่บนเครื่องคอมพิวเตอร์เครื่องอื่นในเครือข่ายไม่ได้เพิ่งมาเกิดขึ้นเพราะเว็บเซอร์วิส แต่แนวคิดดังกล่าวมีมานานแล้ว และก็มีเทคโนโลยีต่าง ๆ ที่ถูกพัฒนาขึ้นเพื่อนำมาใช้ในงานดังกล่าว ซึ่งเว็บเซอร์วิสก็ถูกพัฒนาขึ้นมาโดยใช้เทคโนโลยีเหล่านี้เป็นฐาน เทคโนโลยีดังกล่าวเริ่มมาตั้งแต่ การเรียกโพรซีเยอร์จากระยะไกล (Remote Procedure Call) หรือเรียกย่อ ๆ ว่าอาร์พีซี (RPC)  การเรียกใช้เมท็อดจากระยะไกล (Remote Method Invocation) หรือเรียกย่อ ๆ ว่าอาร์เอ็มไอ (RMI) และ Common Object Request Broker Architecture  (CORBA) (อันนี้ขอไม่แปลแล้วกันนะครับ ยังคิดคำแปลที่ถูกใจไม่ได้:)) เป็นต้น ซึ่งผมหวังว่าจะมีโอกาสได้เขียนให้ได้อ่านกันต่อไป แต่ในวันนี้ผมจะมาพูดถึง อาร์เอ็มไอครับ 

อาร์เอ็มไอก็คือเทคโนโลยีที่พัฒนาขึ้นมาเพื่อให้เราเขียนโปรแกรมเพื่อเรียกใช้เมท็อดจากออบเจ็กต์ที่อยู่บนคนละเครื่องกับโปรแกรมของเรา โดยอาร์เอ็มไอจะช่วยให้การเรียกใช้ออบเจ็กต์ดังกล่าวทำได้ในลักษณะที่เหมือนกับการเรียกใช้เมท็อดจากอ็อบเจกต์ที่อยู่บนเครื่องเดียวกันกับโปรแกรมของเรา นั่นคือ อาร์เอ็มไอช่วยให้เราไม่ต้องไปยุ่งเกี่ยวกับเรื่องพื้นฐานของระบบเครือข่าย เช่นซ็อกเก็ตไม่ต้องยุ่งเกี่ยวกับการแปลงข้อมูลเพื่อให้อยู่ในรูปแบบที่ส่งผ่านเครือข่ายได้ ไม่ต้องยุ่งเกี่ยวกับการที่จะต้องเขียนโปรแกรมเพื่อค้นหาและเรียกใช้เมท็อดจากออกเจ็กต์ที่อยู่ในเครือข่ายด้วยตัวเราเอง (ซึ่งถ้าต้องทำเองนี่ไม่สนุกแน่ครับ) ดังนั้นเราจึงสามารถที่จะมุ่งความสนใจไปในการเขียนโปรแกรมเพื่อแก้ปัญหาที่เราต้องการเพียงอย่างเดียว   

แน่นอนครับ ภาษาจาวา (Java) ซึ่งถูกพัฒนาขึ้นมาให้เป็นภาษาที่รองรับการเขียนโปรแกรมผ่านเครื่อข่ายย่อมไม่พลาดที่จะสนับสนุนแนวคิดนี้ และก็มีเทคโนโลยีที่เรียกว่า จาวาอาร์เอ็มไอ (Java RMI) มาให้เราได้ใช้กันจาวาอาร์เอ็มไอ จะทำให้อ็อบเจกต์ที่ถูกเขียนขึ้นมาด้วยภาษาจาวาแต่ทำงานกันอยู่บนคนละเครื่องในเครือข่าย สามารถที่จะค้นหากันและเรียกใช้กันได้เหมือนกับว่ามันทำงานอยู่บนเครื่องเดียวกัน โดยสถาปัตยกรรมของจาวาอาร์เอ็มไอแสดงได้ดังรูปต่อไปนี้ครับ 


RMI Architecture


จากรูปขออธิบายคร่าว ๆ ดังนี้นะครับไคลเอนต์ (Client)  คือโปรแกรมที่เรียกใช้อ็อบเจกต์ที่อยู่บนอีกเครื่องหนึ่ง เซิร์ฟเวอร์ (Server) ก็คืออ็อบเจกต์ที่ต้องการจะเรียกใช้ สตับ (Stub) คือส่วนที่ทำหน้าที่เป็นตัวแทนของอ็อบเจกต์ที่เราจะเรียกใช้ คือไคลเอนต์จะเรียกใช้อ็อบเจกต์บนฝั่งเซิร์ฟเวอร์ผ่านทางสตับ ส่วนสเกเลตัน (Skeleton) คือตัวแทนของไคลเอนต์บนฝั่งเซิร์ฟเวอร์ สเกเลตันจะเป็นส่วนที่เรียกใช้อ็อบเจกต์บนฝั่งเซิร์ฟเวอร์ ถ้าจะสรุปให้เข้าใจง่าย ๆ ก็คือไคลเอนต์เรียกใช้อ็อบเจกต์ที่ต้องการผ่านทางสตับ สตับจะติดต่อกับสเกเลตันเพื่อให้เรียกใช้อ็อบเจกต์ดังกล่าว 

ทั้งสตับและสเกเลตันยังมีหน้าที่ในการแปลงข้อมูลการเรียกใช้ และค่าที่ส่งกลับมาจากการทำงานของอ็อบเจกต์ให้อยู่ในรูปที่ส่งผ่านเครือข่ายได้ (ข้อมูลที่ส่งผ่านเครือข่ายจะต้องอยู่ในรูปของสายธารตัวอักษร (stream of byte)) และแปลงข้อมูลที่ได้รับจากเครือข่ายให้กลับมาในรูปแบบที่โปรแกรมเข้าใจ ซึ่งการทำงานดังกล่าวเรียกว่า มาร์แชล (Marshal) และ อันมาร์แชล (Unmarshal) ตามลำดับ  

ตัวอย่างการทำงานของสตับและสเกเลตันแสดงได้ดังตัวอย่างนี้ สมมติโปรแกรมฝั่งไคลเอนต์เขียนประโยคต่อไปนี้เพื่อเรียกใช้อ็อบเจกต์บนฝั่งเซิร์ฟเวอร์ 

obj.add(1, 2);  

สตับจะแปลงประโยคดังกล่าวให้อยู่ในรูปสายธารของตัวอักษรเพื่อส่งผ่านเครือข่ายไป เมื่อการเรียกใช้ดังกล่าวไปถึงฝั่งเซิร์ฟเวอร์ สเกเลตันจะแปลงประโยคดังกล่าวกลับมาและเรียกใช้เมท็อด add()  ถ้าเมท็อด add() ต้องส่งค่ากลับให้ไคลเอนต์ สเกเลตันจะแปลงค่าที่จะส่งกลับนั้นให้อยู่ในรูปของสายธารตัวอักษร และส่งผ่านเครือข่ายไปเมื่อข้อมูลมาถึงฝั่งไคลเอนต์ สตับก็จะแปลงข้อมูลกลับให้อยู่ในรูปที่โปรแกรมเข้าใจและส่งผ่านให้ไคลเอนต์ต่อไป 

อีกสองส่วนจากรูปที่ยังไม่ได้กล่าวถึงคือ รีจิสตรีอินเทอร์เฟซ (registry interface) และอาร์เอ็มไอรีจิสตรี (RMI registry) ซึ่งจะขอเรียกสั้น ๆ ว่ารีจิสตรี รีจิสตรีถ้าพูดง่าย ๆ ก็คือโปรแกรมที่ช่วยลงทะเบียนและค้นหาอ็อบเจ็กต์ กล่าวคืออ็อบเจกต์ที่ต้องการจะให้บริการผ่านเครือข่ายจะต้องลงทะเบียนกับรีจิสตรี ซึ่งการลงทะเบียนกับรีจิสตรีนั้นก็คือการเชื่อมชื่อของอ็อบเจกต์เข้ากับอ็อบเจกต์เรฟเฟอร์เรนซ์ (object reference) ซึ่งเมื่อไคลเอนต์ต้องการค้นหาอ็อบเจกต์ ก็จะเอาชื่ออ็อบเจกต์ที่ต้องการมาค้นในรีจิสตรี  และรีจิสตรีก็จะส่งอ็อบเจกต์เรฟเฟอร์เรนซ์กลับไปให้กับไคลเอนต์ เพื่อให้ไคลเอนต์เรียกใช้อ็อบเจกต์ที่ต้องการได้ ไคลเอนต์และเซิร์ฟเวอร์จะติดต่อกับรีจิสตรีผ่านทางรีจิสตรีอินเทอร์เฟซ 

ถึงตรงนี้ถ้ายังงงอยู่ไม่เป็นไรครับ ผมคิดว่าน่าจะเห็นภาพชัดเจนขึ้นเมื่อเราได้เขียนโปรแกรมกันจริง ๆ ในตอนต่อไป แต่ตอนนี้ผมขอตอบคำถามที่หลายคนอาจจะสงสัยกันนะครับว่า สรุปแล้วการเขียนโปรแกรมจาวาอาร์เอ็มไอนี่เราต้องเขียนโปรแกรมตรงไหนบ้าง เราต้องเขียนโปรแกรมสตับและสเกเลตันเองหรือเปล่า อาร์เอ็มไอรีจิสตรีจะไปหามาจากไหน คำตอบคืออย่างนี้ครับ โปรแกรมอาร์เอ็มไอรีจิสตรีจะมากับจาวาเจดีเค  (Java JDK) ที่เราโหลดมาใช้ในการพัฒนาโปรแกรมด้วยภาษาจาวาอยู่แล้ว ส่วนสตับและสเกเลตันเราก็ไม่ต้องเขียนเองครับ ถ้าเราใช้จาวาก่อนเวอร์ชัน 5 เราจะใช้โปรแกรมที่ชื่อว่าอาร์เอ็มไอซี (rmic) ช่วยสร้างให้เรา ซึ่งโปรแกรมนี้ก็มากับจาวาเจดีเคอีกเช่นกัน แต่ตั้งแต่จาวาเวอร์ชัน 5.0 เป็นต้นไปเราไม่ต้องใช้ rmic แล้วนะครับเพราะสตับและสเกเลตันจะถูกจัดการโดนจาวารันไทม์ (Java Runtime)

สรุปก็คือในการเขียนจาวาอาร์เอ็มไอเราแทบจะเขียนโปรแกรมภาษาจาวาไปตามปกติ ผมใช้คำว่าแทบจะก็เพราะมันอาจจะมีบางส่วนที่ต้องศึกษาเพิ่มเติม เช่นการติดต่อกับรีจิสตรีอินเทอร์เฟซ การเขียนอินเทอร์เฟซเพื่อให้รองรับการเรียกใช้แบบข้ามเครื่องเป็นต้น 

เมื่อถึงตรงนี้ก็หวังว่าจะเห็นภาพรวมของจาวาอาร์เอ็มไอกันแล้วนะครับ ผมก็ขอจบแต่เพียงเท่านี้ก่อน เขียนมานานจนเมื่อยแล้ว พบกันใหม่เมื่อชาติต้องการ เอ๊ยไม่ใช่พบกันใหม่ในตอนที่สองครับ เราจะมาสร้างอ็อบเจกต์บนฝั่งเซิร์ฟเวอร์กัน


วันศุกร์ที่ 17 มกราคม พ.ศ. 2557

การสร้าง Administered Object สำหรับ JMS Application บน GlassFish Server

ก่อนที่เราจะเขียนโปรแกรมในพาราไดม์ที่เป็น Messaging System ด้วย Java หรือที่เราจะเรียกว่า JMS Application สิ่งที่ต้องทำก่อนก็คือการสร้าง Administered Object ซึ่งอ็อบเจกต์เหล่านี้ไม่ได้ถูกสร้างในโปรแกรมที่เราเขียนขึ้น แต่จะต้องถูกสร้างผ่านเครื่องมือที่โปรแกรมเว็บเซิร์ฟเวอร์ที่รองรับ JMS Application เตียมไว้ให้ และอ็อบเจกต์เหล่านี้จะต้องถูกสร้างก่อนที่เราจะเขียนโปรแกรม ในบทความนี้จะแสดงให้เห็นขั้นตอนการสร้างอ็อบเจกต์ดังกล่าว ซึ่งอ็อบเจกต์ดังกล่าวแบ่งเป็นสองกลุ่มคือ connection factories และ destination มาดูขั้นตอนการสร้างอ็อบเจกต์เหล่านี้บน GlassFish Server กันครับ

1. เข้าสู่หน้า Admin Console ของ Glass Fish แล้วคลิกที่เมนู Resources


2. สร้าง Connection Factory Object โดยคลิกที่ Connection Factories


3. คลิกที่ปุ่ม new เพื่อสร้าง Connection Factory Object


4. ป้อนชื่อ JNDI (JNDI Name) และประเภททรัพยากร (Resource Type) สำหรับชื่อเราจะใส่อะไรก็ได้ แต่
รูปแบบที่เป็นที่นิยมกันคือใช้ jms/ชื่อที่ต้องการ ส่วนประเภททรัพยากรเราเลือกเป็น ConnectionFactory ถ้าเราต้องการจะใช้อ็อบเจกต์นี้เพื่อสร้าง destination ที่เป็นได้ทั้ง topic และ queue จากนั้นคลิกที่ปุ่ม OK


5. สร้าง Destination อ็อบเจกต์ โดยกลับไปที่ JMS Resources และเลือก Destination Resources

6. สร้าง Destination Object โดยคลิกที่ปุ่ม New



 7. ป้อนข้อมูลเกี่ยวกับ Destination อ็อบเจกต์ที่ต้องการ โดยเลือกประเภทของทรัพยากรให้ตรงตามที่ต้องการว่าจะให้เป็น topic หรือ queue จากนั้นคลิกที่ปุ่ม OK


ก็เป็นอันว่าเรียบร้อยครับตอนนี้เราก็ได้ Administered Object เพื่อใช้กับ JMS Application เรียบร้อยแล้ว ตอนนี้ก็สามารถลงมือเขียน JMS Application กันได้แล้ว

วันพฤหัสบดีที่ 28 พฤศจิกายน พ.ศ. 2556

การเขียนเว็บแอพพลิเคชันด้วย Servlet และ JSP ด้วย NetBeans

วันนี้จะนำเสนอการสร้างเว็บแอพพลิเคชันง่าย ๆ โดยใช้ Servlet และ JSP โดยใช้ NetBeans เป็นตัวสร้างนะครับ สำหรับ NetBeans ที่จะใช้นี้ต้องเป็น NetBeans ที่รองรับ Java EE นะครับ ถ้าใครจะดาวน์โหลดก็ใช้อันนี้นะครับ

 

ส่วนเวอร์ชันที่ใช้ไม่ต้องเป็นเวิร์ชันล่าสุดก็ได้นะครับ เอาเป็นสัก 7 ขึ้นมาก็ได้ 6 ดูจะเก่าไป หลังจากดาวน์โหลดแล้วติดตั้งแล้วก็มาเริ่มกันเลยครับ วันนี้เราจะมาสร้างแอพลิเคชั้นง่าย ๆ ตามคอนเซ็ปของบล็อกกันครับ นั่นคือโปรแกรมแปลงอุณหภูมิจากฟาเรนต์ไฮต์เป็นเซลเซียส โปรแกรมของเราจะมีการทำงานดังนี้ครับเริ่มจากมีหน้าจอให้ผู้ใช้ป้อนอุณภูมิเป็นฟาเรนต์ไฮต์ ดังรูป



เมื่อผู้ใช้ป้อนอุณหภูมิและกดปุ่ม Submit จะแแสดงหน้าจอดังรูปถัดไป


มาเริ่มสร้างกันเลยครับ เริ่มจากสร้างโปรเจกต์กันก่อนดังรูป โดยให้เลือกเป็น Java Web -> Web Application ดังรูป





จากนั้นให้เลือก Server เป็น GlassFish หรือจะเลือกเป็น Tomcat ก็ได้

จะได้โปรเจกต์ ซึ่ง NetBeans จะมีไฟล์ HTML ชื่อ index.html ไว้ให้ ซึ่งให้เราเขียนโค้ดสำหรับฟอร์มรับค่าอุณหภูมิเป็นฟาเรนต์ไฮต์ดังรูป


   ถ้าดูโค้ดในรูปไม่ชัดก็ดูได้จากด้านล่างนี้


<html>
    <head>
        <title>Fahrenheit to Celcius </title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
    </head>
    <body>
        <form name="main" action="FahrenheitToCelcius">
            Fahrenheit: <input type="text" name="fahrenheit" value="" size="10" />
            <br/>
            <input type="submit" value="Submit" name="submit" />
        </form>
    </body>
</html>

ให้สังเกตุค่าที่กำหนดให้กับแอททริบิวต์ action ของแท็ก form ไว้ให้ดี เพราะนี่จะเป็นชื่อของ Servlet ที่เราจะสร้าง และขอให้สังเกตุที่กำหนดให้กับแอทริบิวต์ name ของแท็ก input ไว้ด้วยเพราะเราจะต้องใช้เพื่อนำค่าที่ผู้ใช้ป้อนไปประมวลผล

ขั้นต่อไปจะสร้าง Servlet โดยคลิกขวาที่ Source Package ในตัวโปรเจกต์เลือก New -> Servlet



ตั้งชื่อคลาสให้ตรงกับค่าที่อยู่ใน แอททริบิวต์ action ของแท็ก form แล้วคลิก Next ย้ำนะครับว่า Next อย่าเพิ่ง Finish เพราะเราควรจะกำหนดค่านี้ให้อยู่ใน deployment descriptor ด้วย

ให้เลือก Add Information to deployment descriptor (web.xml) ด้วยแล้วจึงคลิก Finish (ขอไม่อธิบายนะครับว่าคืออะไร เพราะมันจะยาวเกินขอบเขตของ Tutorial)

NetBeans จะสร้างคลาสให้ ซึ่งส่วนที่เราจะเขียนโค้ดให้ Servlet คือเมท็อด ProcessRequest() ดังรูป


ถ้าดูไม่ชัดสามารถดูได้จากโค้ดข้างล่างนี้ครับ

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
           response.setContentType("text/html;charset=UTF-8");
          String fahrenheitStr = request.getParameter("fahrenheit");
         double fahrenheit = Double.parseDouble(fahrenheitStr);
         DecimalFormat twoDigits = new DecimalFormat("0.00");
        double celcius = (5.0 / 9 ) * (fahrenheit - 32);
        String celciusStr = twoDigits.format(celcius);
        fahrenheitStr = twoDigits.format(fahrenheit);
        request.setAttribute("fahrenheit", fahrenheitStr);
        request.setAttribute("celcius", celciusStr);
        request.getRequestDispatcher("result.jsp").forward(request, response);
     
    }

ตรงนี้ขอขยายความเล็กน้อยครับ ว่าจริง ๆ แล้วตามหลักการจริง ๆ ในคลาส Servlet ที่สร้างขึ้นนี้เราจะต้อง Override เมท็อด doGet() หรือ doPost() ขึ้นอยู่กับว่าตอนที่ส่ง HTTP Request มาใช้เมท็อด Get หรือ Post แต่ NetBeans เลือก Override ทั้งสองเมท็อดนี้ให้มาเรียกเมท็อด processRequest() นั่นคือไม่ว่า HTTP Request ที่ส่งมาจะเป็นเมท็อด Get หรือ Post เราก็จะเขียนโปรแกรมที่เมท็อด ProcessRequest()

ในส่วนของโค้ดที่เราเขียนขออธิบายเป็นบรรทัดต่อบรรทัดดังนี้ครับ

String fahrenheitStr = request.getParameter("Fahrenheit"); รับค่าที่ผู้ใช้ป้อนเข้ามา ซึ่งในพารามิเตอร์ของเมท็อด getParamater() เราจะระบุชื่อให้ตรงกับที่เราตั้งไว้ใน Form และให้เข้าใจนะครับว่าสิ่งที่ผู้ใช้ป้อนเข้ามาจะอยู่ในรูปแบบสตริง

double fahrenheit = Double.parseDouble(fahrenheitStr);  แปลงสตริงให้เป็นตัวเลข
double celcius = (5.0 / 9) * (fahrenheit -32); แปลงฟาเรนต์ไฮต์เป็นเซลเซียส

DecimalFormat twoDigits = new DecimalFormat("0.00"); เป็นการสร้างออบเจ็กต์ที่จะใช้ในการจัดรูปแบบการแสดงผลตัวเลขให้เป็นทศนิยมสองตำแหน่ง

String celciusStr = twoDigits.format(celcius);
fahrenheitStr = twoDigits.format(fahrenheit);
สองบรรทัดข้างบนก็คือการจัดรูปแบบผลลัพธ์ครับทั้งฟาห์เรน"ฮต์และเซลเซียส

request.setAttribute("fahrenheit", fahrenheitStr);
request.setAttribute("celcius", celciusStr);  

สร้างแอททริบิวต์เพื่อส่งต่ออุณหภูมิไปแสดงผลในหน้า JSP ที่เราจะสร้างต่อไป

 request.getRequestDispatcher("/result.jsp").forward(request, response); ส่งต่อการทำงานไปยังหน้า JSP ชื่อ result.jsp

ขั้นสุดท้ายซะทีครับ สร้าง result.jsp เริ่มจากคลิกขวาที่ โฟลเดอร์ Web Pages เลือก New -> JSP  ดังรูป




ในช่อง File Name ให้ใส่ชื่อ result ไม่ต้องใส่ .jsp นะครับ แล้วกดปุ่ม Finish จากนั้น NetBeans จะสร้างไฟล์ชื่อ result.jsp ให้เราเขียนโค้ดดังนี้





<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <% out.print(request.getAttribute("fahrenheit") + " Fahrenheit is " + request.getAttribute("celcius") + " Celcius"); %>
    </body>
</html>


ให้สังเกตุวิธีการเรียกใช้ค่าพารามิเตอร์และแอทริบิวต์ที่ถูกส่งมาจาก Servlet ในขั้นตอนก่อนหน้า และจะเห็นว่าเราสามารถใช้ ตัวแปร request ได้เลย เพราะ request เป็นหนึ่งใน implicit object ของ JSP ครับ

เอาล่ะครับ เสร็จเสียที ตอนนี้ก็คลิกขวาที่ชื่อโปรเจกต์ แล้วเลือก run ได้เลย จะได้หน้าฟอร์มแสดงในเว็บเบราว์เซอร์ของเราตามที่แสดงในรูปแรกครับ