วันพฤหัสบดีที่ 3 กันยายน พ.ศ. 2563

การเขียนโปรแกรมเครือข่ายด้วยซ็อกเก็ตตอนที่ 11 เขียนโปรแกรม TCP Iterative Server และ TCP Client ด้วยภาษา Python

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

สำหรับบทความนี้ผมจะแสดงตัวอย่างโปรแกรมที่ทำงานเหมือนกับโปรแกรมภาษา Java ในตอนที่ 3 และตอนที่ 4 ที่มีการทำงานโดยการที่ผู้ใช้จะพิมพ์ข้อความเป็นภาษาอังกฤษทางฝั่งไคลเอนต์ (client) จากนั้นส่งข้อมูลมาให้เซิร์ฟเวอร์ (server) ซึ่งจะแปลงตัวอักษรดังกล่าวให้เป็นตัวพิมพ์ใหญ่ทั้งหมด แล้วส่งกลับไปให้ฝั่งไคลเอนต์แสดงผล 

โปรแกรมฝั่งเซิร์ฟเวอร์แสดงได้ดังนี้ครับ 

01: # tcpserver.py

02: import socket

03: welcomeSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

04: welcomeSocket.bind(("localhost", 6789))

05: welcomeSocket.listen(5)

06: while True:

07:     print("The server is waiting")

08:     connectionSocket, address = welcomeSocket.accept()

09:     clientSentenceBytes = connectionSocket.recv(4096)

10:     clientSentence = clientSentenceBytes.decode("utf-8")

11:     connectionSocket.send(str.encode(clientSentence.upper()))

12:     connectionSocket.close()

บรรทัดที่ 2 คือการอิมพอร์ต socket โมดูล บรรทัดที่ 3 สร้างเซิร์ฟเวอร์ซ็อกเก็ตเพื่อรอรับการติดต่อ โดยพารามิเตอร์ที่ใช้ในการสร้างมี 2 ตัว ตัวแรกคือ socket.AF_INET ซึ่งหมายถึงเราระบุว่าจะใช้ IP เวอร์ชัน 4 ตัวที่สอง socket.SOCK_STREAM หมายถึงโปรโตคอล TCP 

บรรทัดที่ 4 ระบุชื่อโฮสต์และพอร์ตที่จะรอบรับการติดต่อ ใช้ localhost เป็นชื่อโฮสต์ เพราะต้องการจะให้การติดต่ออยู่ภายในเครื่องเดียวกันเท่านั้น ถ้าต้องการให้เครื่องภายนอกติดต่อเข้ามาได้ให้ใช้ สตริงว่าง ("") แทน ส่วนพอร์ตที่จะรอรับการติดต่อคือ 6789   

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

จากนั้นเซิร์ฟเวอร์ก็จะเข้าสู่ลูปไม่รู้จบตามการทำงานของเซิร์ฟเวอร์ทั่ว ๆ ไป โดยในลูปนี้ บรรทัดที่ 8 เซิร์ฟเวอร์จะบล็อกรอรับการติดต่อจากไคลเอนต์  โดยเมื่อมีไคลเอนต์ติดต่อเข้ามา จะมีการสร้างซ็อกเก็ตเชื่อมต่อ (connection socket) อ้างถึงโดยตัวแปร connectionSocket และหมายเลขไอพี (IP Address) ของเครื่องไคลเอนต์ที่ติดต่อเข้ามา อ้างถึงโดยตัวแปร address 

บรรทัดที่  9 รออ่านข้อมูลที่ไคลเอนต์จะส่งเข้ามา โดยในที่นี้จะรับข้อมูลขนาดไม่เกิน 4096 ไบต์ คำแนะนำสำหรับขนาดที่เราควรระบุก็คือเป็น 2 ยกกำลังที่ไม่มากนัก ในที่นี่ใช้ 2 ยกกำลัง 12 ก็คือ 4096 ไบต์ครับ สมมติถ้าเราต้องการรับข้อมูลขนาดใหญ่ แทนที่จะกำหนดค่าให้ใหญ่ ๆ วิธีที่ดีกว่าก็คือการวนลูปอ่านข้อมูล ครับ ซึ่งในตัวอย่างนี้ผมจะขอละไว้นะครับ เพราะต้องการแสดงแค่แนวคิดการเขียนโปรแกรมเบื้องต้น 

บรรทัดที่ 10 เป็นการ decode ข้อมูลที่อ่านมาจากซ็อคเก็ตที่อยู่ในรูปสตรีมของไบต์ (stream of bytes) ให้เป็นสตริง เพื่อที่จะได้เรียกใช้ฟังก์ชัน upper() เพื่อแปลงข้อมูลที่รับมาให้เป็นตัวอักษรภาษาอังกฤษตัวใหญ่ 

บรรทัดที่ 11 จะซับซ้อนนิดหนึ่งนะครับ เพราะทำหลายอย่าง ในส่วน clientSentence.upper() ก็คือการแปลงข้อมูลเป็นตัวอักษรตัวใหญ่ ในส่วน str.encode(clientSentence.upper()) ก็คือการทำให้สตริงที่จะส่งไป (ในที่นี้คือสตริงที่เราแปลงเป็นตัวใหญ่) อยู่ในรูปสายธารของไบต์ จากนั้นก็ใช้ฟังก์ชัน send() ส่งข้อมูลผ่านซ็อกเก็ตกลับไปให้ไคลเอนต์ 

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

คราวนี้มาดูโปรแกรมฝั่งไคลเอนต์กันครับ 

01: # tcpclient.py

02: import socket

03: sentence = input("Please enter words: ")

04: clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

05: clientSocket.connect(("localhost", 6789))

06: clientSocket.send(str.encode(sentence))

07: modifiedSentenceBytes = clientSocket.recv(4096)

08: modifiedSentence = modifiedSentenceBytes.decode("utf-8")

09: clientSocket.close()

10: print(modifiedSentence)


บรรทัดที่ 2 คือการอิมพอร์ต socket โมดูล บรรทัดที่ 3 รอรับข้อมูลที่ผู้ใช้ป้อน โดยแสดงข้อความ Please enter words เพื่อบอกผู้ใช้ ค่าที่ผู้ใช้ป้อนจะเก็บในตัวแปร sentence 

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

บรรทัดที่ 5 สร้างการเชื่อมต่อกับโปรแกรมเซิร์ฟเวอร์โดยใช้หมายเลขพอร์ต 6789 ตามที่โปรแกรมเซิร์ฟเวอร์ใช้ 

บรรทัดที่ 6 ส่งข้อมูลผ่านซ็อกเก็ต โดยแปลงข้อมูลให้อยู่ในรูปสายธารของไบต์ 

บรรทัดที่ 7 รออ่านข้อมูลที่เซิร์ฟเวอร์จะตอบกลับมา บรรทัดที่ 8 แปลงข้อมูลจากสายธารของไบต์เป็นสตริง 

บรรทัดที่ 9 ปิดซ็อกเก็ตเชื่อมต่อ และบรรทัดที่ 10 พิมพ์ผลลัพธ์ออกมางจอภาพ จากนั้นโปรแกรมไคลเอนต์จะจบการทำงาน 

ครางนี้มาลองรันโปรแกรมกันดูครับ ผมจะรันโปรแกรมจากเทอร์มินอลที่เปิดในโปรแกรม VS Code นะครับ แต่ใครจะรันผ่านเทอร์มินัลที่ตัวเองใช้อย่าง command prompt ของ Windows หรือเทอร์มินัลของ Mac หรือ Linux ก็ได้ครับ คำสั่งที่ใช้รันโปรแกรมเซิร์ฟเวอร์ก็คือ python tcpserver.py ดังรูปครับ 


เมื่อรันเสร็จจะเห็นว่าเซิร์ฟเวอร์จะพิมพ์คำว่า  The server is waiting และหยุดรอรับการติดต่อจากไคลเอนต์

ต่อไปไปรันไคลเอนต์กันครับ ก็ให้เปิดเทอร์มินัลขึ้นมาอีกหน้าต่างหนึ่งครับ และใช้คำสั่ง python tcpclient.py ดังรูปครับ 


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

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

สำหรับโค้ดของโปรแกรมโหลดได้จากที่นี่ครับ 

ไม่มีความคิดเห็น:

แสดงความคิดเห็น