18 ก.ย. 2020 เวลา 19:03 • วิทยาศาสตร์ & เทคโนโลยี
การเขียนโปรแกรมเครือข่ายด้วยซ็อกเก็ต (socket) ตอนที่ 4: TCP Concurrent Server
จากโปรแกรมเซิร์ฟเวอร์ในตอนที่ผ่านมา จัดเป็นเซิร์ฟเวอร์แบบ iterative นั่นคือให้บริการไคลเอนต์ได้ครั้งละหนึ่งตัว ถ้ามีไคลเอนต์มากกว่าหนึ่งตัว ตัวที่เข้ามาทีหลังจะต้องเข้าคิวรอจนกว่าจะถึงคิวตัวเอง ซึ่งเซิร์ฟเวอร์แบบนี้ไม่เหมาะสมกับงานที่ไคลเอนต์และเซิร์ฟเวอร์ต้องติดต่อกันเป็นเวลานาน ลองนึกภาพว่าถ้า Gmail ให้บริการแบบนี้ดูนะครับว่ามันจะเป็นยังไง ดังนั้นงานแบบนี้จะต้องเขียนเซิร์ฟเวอร์ที่ทำงานแบบพร้อมกัน (Concurrent) ครับ
ซึ่งการเขียนก็ไม่ได้ยากอะไร แนวคิดที่ผ่านมาในตอนต่าง ๆ ยังใช้ได้หมด แต่สิ่งที่เราจะเพิ่มเข้ามาคือเราจะนำเธรด (Thread) มาใช้ครับ หลักการก็คือหลังจากที่เซิร์ฟเวอร์รับการติดต่อจากไคลเอนต์เข้ามาแล้วแทนที่จะไปให้บริการไคลเอนต์เองเหมือนที่ผ่านมา ก็จะสร้างเธรด ขึ้นมาให้บริการไคลเอนต์แต่ละตัว ไปลองดูโค้ดกันครับ
02: import java.io.*;
03: import java.net.*;
04: import java.util.*;
05: public class TCPConcurrentServer {
06: public static void main(String argv[]) {
07: String clientSentence;
08: String capitalizedSentence;
09: ServerSocket welcomeSocket = null;
10: try {
11: welcomeSocket = new ServerSocket(6789);
12: }
13: catch (IOException e) {
14: System.out.println("Cannot create a welcome socket");
15: System.exit(1);
16: }
17: while(true) {
18: try {
19: System.out.println("The server is waiting ");
20: Socket connectionSocket = welcomeSocket.accept();
21: EchoThread echoThread = new EchoThread(connectionSocket);
22: echoThread.start();
23: }
24: catch (IOException e) {
25: System.out.println("Cannot create this connection");
26: }
27: }
28: }
29: }
จะเห็นว่าโค้ดเหมือนของ iterative server แทบทุกอย่าง สิ่งที่แตกต่างคือบรรทัดที่ 21 ซึ่งมีการสร้างอ็อบเจกต์ของคลาส EchoThread โดยส่งผ่านซ็อกเก็ตเชื่อมต่อไปให้อ็อบเจกต์ดังกล่าว จากนั้นบรรทัดที่ 22 สั่งให้เธรดเริ่มทำงาน โดยส่วนที่ใช้ในการให้บริการไคลเอนต์ในการแปลงตัวอักษรตัวเล็กเป็นตัวใหญ่ และส่งค่ากลับไปให้ไคลเอนต์จะอยู่ในคลาสนี้ และเมื่อเธรดทำงานตัวเซิร์ฟเวอร์ก็จะมีอิสระในการไปรับการติดต่อของไคลเอนต์ตัวอื่น และให้บริการไคลเอนต์ตัวอื่นโดยใช้เธรดเช่นเดียวกัน โค้ดของคลาส EchoThread แสดงได้ดังนี้ครับ
02: import java.io.*;
03: import java.net.*;
04: import java.util.*;
05: public class EchoThread extends Thread {
06: private Socket connectionSocket;
07: public EchoThread(Socket connectionSocket) {
08: this.connectionSocket = connectionSocket;
09: }
10: public void run() {
11: Scanner inFromClient = null;
12: DataOutputStream outToClient = null;
13: try {
14: inFromClient = new Scanner(connectionSocket.getInputStream());
15: outToClient =
16: new DataOutputStream(connectionSocket.getOutputStream());
17: String clientSentence = inFromClient.nextLine();
18: String capitalizedSentence = clientSentence.toUpperCase() + '\n';
19: outToClient.writeBytes(capitalizedSentence);
20:
21: }
22: catch (IOException e) {
23: System.err.println("Closing Socket connection");
24: }
25: finally {
26: try {
27: if (inFromClient != null)
28: inFromClient.close();
29: if (outToClient != null)
30: outToClient.close();
31: if (connectionSocket != null)
32: connectionSocket.close();
33: }
34: catch (IOException e) {
35: e.printStackTrace();
36: }
37: }
38: }
39: }
จะเห็นว่าบริการที่เคยอยู่ใน iterative server ถูกย้ายมาอยู่ในเมท็อด run() ซึ่งจะถูกเรียกใช้เมื่อเธรดทำงาน ส่วนโค้ดของไคลเอนต์นั้นเหมือนเดิมครับ ไม่ต้องแก้อะไร
โค้ดสามารถดูโหลดได้จาก https://github.com/ajsarun/tcp_concurrent_java
หมายเหตุ: บทความนี้มีการเปลี่ยนแปลงนิดหน่อยจากบทความ https://computingblog.intakosum.net/2013/12/socket-6-tcp-concurrent-server.html ที่ผมเขียนไว้ในบล็อกของผมครับ
อ่านเรื่องอื่น ๆ ทางด้านวิทยาการคอมพิวเตอร์ในส่วนที่เกี่ยวข้องกับวิชาที่ผมสอน หรือสนใจได้ที่ https://computingblog.intakosum.net

ดูเพิ่มเติมในซีรีส์

โฆษณา