การใช้ Socket Class เพื่อส่งและรับข้อมูลกับ Server HTTP ใน .NET Core 2 และ C# 7

สำหรับบทความนี้จะกล่าวถึงคุณสมบัติที่ถูกปรับปรุงใหม่ของภาษาซีชาร์ป 7.0 และ .NET Core 2.0 ในประเด็นที่เกี่ยวข้องกับการสื่อสารข้อมูลในเครือข่าย
ภาพหน้าปกบทความ การใช้ Socket Class เพื่อส่งและรับข้อมูลกับ Server  HTTP
ทักษะ (ระบุได้หลายทักษะ)

การใช้ Socket Class เพื่อส่งและรับข้อมูลกับ Server  HTTP

สำหรับบทความนี้จะกล่าวถึงคุณสมบัติที่ถูกปรับปรุงใหม่ของภาษาซีชาร์ป 7.0  และ .NET Core 2.0 ในประเด็นที่เกี่ยวข้องกับการสื่อสารข้อมูลในเครือข่าย

ตัวอย่างการใช้คลาสซอกเก็ตเพื่อส่งและรับข้อมูลกับเซอฟเวอร์ HTTP

Socket Class

วิธีพื้นฐานสุดของการเขียนโค้ดเพื่อสี่อสารในเครือข่ายทำได้โดยใช้ Socket Class  ซึ่ง
  • มี method และ property ที่เกี่ยวข้องกับการสื่อสารอยู่ภายในเป็นจำนวนมาก
  • สนับสนุน protocol จำนวนมาก อาทิ  IPv4, IPv6, TCP และ UDP
  • สนับสนุนการทำงานทั้งแบบผสานจังหวะ (synchronous) และแบบไม่ผสานจังหวะ (asynchronous)

รูปที่ 1 แสดงตัวอย่างโค้ดที่ใช้ Class Socket เพื่อส่งข้อและรับมูลกับ server  HTTP โดยรันทดสอบใน .NET Core  2.2 รูปนี้คือโค้ดส่วน method  ConnectSocket ซึ่งทำหน้าที่เชื่อมต่อกับ server ด้วย protocol  TCP  method นี้รับพารามิเตอร์สองตัว ตัวแรกคือชื่อของ server  ตัวที่สองคือหมายเลขของพอร์ท TCP ที่ต้องการเชื่อมต่อ
 
บรรทัด 12 ทำหน้าที่อ่านข้อมูลที่เกี่ยวข้องกับ host   ซึ่งเป็น HTTP  server
บรรทัด 14-34 วนการทำงานเพื่ออ่านรายการของ address ทั้งหมด เพื่อหารายการของกลุ่ม address ที่สนับสนุนการทำงาน
สาเหตุที่ต้องหารายการเพราะต้องการคัดกรองกลุ่ม address ที่ไม่สนับสนุนการทำงานออกไป
เช่นในกรณีที่เราต้องการทำงานกับ IPv4 เราจะต้องคัดกรองกลุ่ม address ที่เป็น IPv6 ออกไป หากไม่ทำเช่นนั้นจะเกิด errorตอนที่โค้ดทำงาน
เมธอด SocketSendReceive
รูปที่ 2  method  SocketSendReceive ทำหน้าที่แสดงวิธีร้องของหน้าเว็บไปยัง host ที่เป็น HTTP  server  จากนั้นก็วนรอรับการตอบสนองจาก host  
 
บรรทัด 45 สร้าง Socket ด้วยการกำหนดชื่อ server แล้วเชื่อมต่อกับ host ด้วยการเรียกหา method  ConnectSocket
บรรทัด 51 ส่งการร้องขอไปยัง server โดยใช้ method  Send
บรรทัด 52 เริ่มรอรับการตอบสนองจาก server
บรรทัด 56-63 วนการทำงานเพื่ออ่านข้อมูล โปรแกรมจะค้างอยู่ในลูปจนกว่าจะอ่านข้อมูลได้ครบทั้งหน้า
การอ่านข้อมูลจาก server ทำได้โดยใช้ method  Receive ซึ่งจะนำข้อมูลมาเก็บไว้ในตัวแปร bytesReceived
และจะได้ตัวเลขขนาดของข้อมูลที่ได้รับมาเก็บไว้ในตัวแปร bytes การทำงานจะซ้ำไม่หยุดตราบเท่านี้ค่าของตัวแปรนี้ยังมากกว่าศูนย์
เมธอด Main
ผลลัพธ์การทำงานของโปรแกรม

รูปที่ 3 คือ method  Main มีโค้ดซึ่งทำหน้าที่เรียกใช้ method  SocketSendReceive
ซึ่งจะเชื่อมต่อกับ host เว็บไซต์ด้วย method  ConnectSocket โดยทำหน้าที่เชื่อมต่อกับ server ด้วย protocol  TCP อ่านหน้าโฮมเพจของเว็บไซต์ด้วยโค้ดภายใน method SocketSendReceive

บรรทัด 73 ตรวจสอบว่าผู้ใช้งานป้อนพิมพ์ชื่อ server  (เช่น www.google.com) เข้ามาตอนเรียกให้โปรแกรมทำงานหรือไม่ ถ้าป้อนเข้ามาก็จะนำไปใช้ในบรรทัดที่ 78
บรรทัด 74 ถ้าผู้ใช้ไม่ได้ป้อนพิมพ์ชื่อ server  มันจะถือว่าคอมพิวเตอร์เครื่องที่กำลังรันโปรแกรมนี้เป็น host เสียเอง
บรรทัด 78 เรียกใช้ method  SocketSendReceive ผลลัพธ์ที่ได้คือหน้าเว็บหนึ่งหน้าเก็บอยู่ในตัวแปร result
บรรทัด 79 แสดงสิ่งที่อยู่ใน result ที่คอนโซล หากท่านป้อนพิมพ์ชื่อ host เป็น www.google.com ท่านจะเห็นผลลัพธ์คล้ายในรูปที่ 4
 

ตัวอย่างโค้ดแสดงวิธีสร้างออพเจ็กต์แบบซอกเก็ต

การสร้าง object แบบ Socket

การเขียนโค้ดเพื่อสร้าง object แบบ Socket ทำได้ด้วยวิธีปรกติเหมือน object ชนิดอื่น ๆ
นั่นคือใช้ตัวกระทำ new แล้วเรียกหา method พิเศษสำหรับการสร้าง object  
ซึ่งก็คือ constructor method  ของ Class Socket ที่ overไว้ให้เลือกใช้ 3 แบบได้แก่
 

  1. Socket(SocketInformation)
  2. Socket(SocketType, ProtocolType)
  3. Socket(AddressFamily, SocketType, ProtocolType)

ตัวอย่างโค้ดในรูปที่ 5 แสดงการ object แบบ Socket ด้วยการเรียกหา method constructor  แบบที่ 3 ซึ่งเป็นแบบที่มีพารามิเตอร์สามตัวได้แก่
 

  • AddressFamily: ทำหน้าที่กำหนดประเภทรายการ address ที่ต้องการ เช่น AppleTalk, Ecma, Unix ฯลฯ
  • SocketType :  ทำหน้าที่กำหนดประเภทของSocketที่ต้องการ เช่น Raw, Stream, Dgram ฯลฯ
  • ProtocolType : ทำหน้าที่กำหนดชนิดของ protocol  เช่น IP, IPv4, IPv6 ฯลฯ

ในตัวอย่างนี้จะให้ AddressFamily เป็นแบบ InterNetwork และให้ SocketType เป็นแบบ Stream และกำหนดให้ ProtocolType เป็นแบบ TCP
บรรทัดที่ 10-15 ประกาศตัวแปรให้เก็บค่าที่จะเขียนไปยัง server
บรรทัดที่ 19 ประกาศตัวแปร s ที่มีไทป์เป็น Socket โดยให้เป็นค่านัลไว้
บรรทัดที่ 20-21 IPAddress และ IPEndPoint คือจุดหมายปลายทางของการเรียกและรับข้อมูล โดยจะใช้ไอพีแรกที่ DNS ส่งมาให้
บรรทัดที่ 19-24 ประกาศและกำหนดค่าให้แก่ตัวแปรทั้งหลายที่จำเป็นต้องใช้ภายในลูปไว้นอกลูป เพื่อไม่ให้เกิดการประกาศซ้ำ ๆ ทุกครั้งของการวน
บรรทัดที่ 23 อ่านข้อมูลจาก DNS ของ host
บรรทัดที่ 24 อ่านไอพี address ทั้งหมดจาก DNS ของ host
บรรทัดที่ 25-41 วนการทำงานเพื่อหาค่า IPAddress และค่า IPEndPoint
บรรทัดที่ 29 สร้าง object ของSocketโดยการเรียกหา method คอนสทรักเตอร์แบบที่ 3 ซึ่งเป็นแบบที่มีพารามิเตอร์ 3 ตัว
บรรทัดที่ 33 เชื่อมต่อกับ host โดยใช้ IPEndPoint ของ host นั้น
บรรทัดที่ 36 การเชื่อมต่อล้มเหลว เลื่อนไปลอง IP address ถัดไป
บรรทัดที่ 40 ส่ง request แบบ GET ไปยัง host
บรรทัดที่ 42-49 การเชื่อมต่อทำได้สำเร็จ วนการทำงานเพื่ออ่านข้อมูลจากหน้าโฮมจนกว่าจะได้รับข้อมูลครบทั้งหมด

ตัวอย่างโค้ดแสดงวิธีสร้างออพเจ็กต์แบบซอกเก็ต (ต่อ)

บรรทัดที่ 51-56 ดัก errorในกรณีที่เป็น error ของตัว Socket
บรรทัดที่ 51-56 ดัก errorในกรณีที่เป็น ArgumentNull
บรรทัดที่ 51-56 ดัก errorในกรณีที่เป็น NullReference
บรรทัดที่ 51-56 ดัก errorในกรณีที่เป็น error อื่น ๆ
บรรทัดที่ 77-80  method  Main ทำหน้าที่เรียก method  DoSocketGet  เพื่อทดสอบการทำงาน โดยส่งชื่อ host Google ไปใช้ในการทดสอบ

ผลลัพธ์การทำงานของโปรแกรม

พารามิเตอร์ addressFamily ทำหน้าที่ระบุลักษณะของ address ที่ object จะนำไปใช้
พารามิเตอร์ socketType เป็นตัวกำหนดชนิดของ object Socket และ
พารามิเตอร์ protocolType ทำหน้าที่กำหนดชนิดของ protocol  

พารามิเตอร์ทั้ง 3 ตัวนี้มีความสัมพันธ์กัน ยกตัวอย่างเช่น
หากเรากำหนด addressFamily ให้เป็นแบบหนึ่ง  protocol ที่ใช้ก็จะต้องเป็นแบบที่สนับสนุน addressFamily ชนิดนั้นด้วย ถ้ากำหนดไว้ไม่สัมพันธ์กันจะเกิด error

การสื่อสารแบบไร้การเชื่อมต่อ

การสื่อสารแบบไร้การเชื่อมต่อ

การสื่อสารในเครือข่ายด้วย Socket แบ่งออกเป็น 2 แบบ
แบบแรก คือ การสื่อสารที่ใช้ protocol  แบบมีการเชื่อมต่อ (connection-oriented protocol เช่นการสื่อสารที่ใช้ protocol  TCP) และ
แบบที่สองคือ การสื่อสารที่ใช้ protocol แบบไร้การเชื่อมต่อ (connectionless protocol เช่นการสื่อสารที่ใช้ protocol  UDP)

ในกรณีที่เป็นการสื่อสารแบบไร้การเชื่อมต่อ เราไม่จำเป็นต้องเขียนโค้ดซึ่งทำหน้าที่คอยรอรับฟังการเชื่อมต่อ
การรับข้อมูลสามารถทำได้โดยใช้ method  ReceiveFrom และการส่งข้อมูลไปยัง host สามารถทำได้โดยใช้ method  SendTo

รูปที่ 8 แสดงโค้ดตัวอย่างการใช้ method  ReceiveFrom และ SendTo เพื่อรับส่งข้อมูลกับ host
บรรทัดที่ 7-21 คือ method  ReceiveForm2 ทำหน้าที่เรียกใช้งาน method  ReceiveFrom เพื่อการรับข้อมูล
บรรทัดที่ 14 สร้าง IpEndPoint เพื่อทำหน้าที่ใช้สอบยันตัวตนของ host
บรรทัดที่ 16 คือโค้ดทำหน้าที่ผูก object Socketเข้ากับเอ็นด์พอยท์ด้วย method  Bind
บรรทัดที่ 19 คำสั่งบรรทัดนี้จะทำให้โปรแกรมค้างจนกว่าจะได้รับข้อมูลครบ

method  ReceiveFrom จะอ่านข้อมูลไปเก็บไว้ในพารามิเตอร์ buffer ส่งค่ากลับเป็นจำนวนไบต์ที่อ่านได้สำเร็จ
method นี้เหมาะนำมาใช้เมื่อต้องการรับข้อมูลจาก host เดียวหรือเหลาย ๆ  host โดยไม่ต้องเชื่อมต่อไว้ก่อนล่วงหน้า  
method นี้มีพารามิเตอร์สามตัวได้แก่
 

  • buffer: เป็นหน่วยเก็บข้อมูลที่จะอ่านมา มีชนิดเป็นไบต์อาร์เรย์
  • socketFlags: เป็นอีนัมทำหน้าที่กำหนดวิธีทำงานของการรับส่งข้อมูล
  • remoteEP: เป็นตัวแปรเก็บค่าค่าอ้างอิงไปยัง host

ก่อนที่จะเรียกใช้ method  ReceiveFrom เราจำเป็นจะต้องผูกSocketกับเอ็นพอยนท์ด้วย method  Bind ก่อน ไม่เช่นนั้นจะเกิด error
เมื่อใช้ method  ReceiveFrom กับการสื่อสารแบบไร้การเชื่อมต่อ มันจะอ่านข้อมูลจากคิวเข้ามาใส่บัฟเฟอร์ของเครือข่าย

ถ้าจำนวนข้อมูลมีปริมาณมากกว่าขนาดของบัฟเฟอร์ มันจะอ่านมาจนเต็มบัฟเฟอร์แล้วแจ้ง error
ถ้า protocol ที่ใช้เป็นแบบเชื่อถือไม่ได้ (unreliable protocol) ข้อมูลส่วนที่ล้นบัฟเฟอร์จะหายไป
แต่ถ้า protocol ที่ใช้เป็นแบบเชื่อถือได้ (reliable protocol)  host จะกักข้อมูลส่วนที่ล้นบัฟเฟอร์ไว้
ทำให้เราสามารถเรียก method  ReceiveFrom อ่านข้อมูลเข้ามาใหม่ได้ โดยกำหนดขนาดขอวงบัฟเฟอร์ให้ใหญ่กว่าเดิม

ในกรณีที่ host ไม่มีข้อมูลจะส่ง  method  ReceiveFrom จะทำให้โปรแกรมค้างจนกว่าจะได้รับข้อมูล
แต่ถ้าโปรแกรมทำงานในโหมดป้องกันการค้าง  method  ReceiveFrom จะจบการทำงานทันทีพร้อมแจ้ง error
เพื่อป้องกันปัญหานี้เราสามารถตรวจสอบก่อนได้ว่ามีข้อมูลอยู่ภายในสแตกเฟรมหรือไม่ ด้วยการดูที่ค่าของ property  Available ที่จะมีค่าเป็นศูนย์หากไม่มีข้อมูล

บทความตอนนี้พูดถึงคุณสมบัติที่ถูกปรับปรุงใหม่ของภาษาซีชาร์ป 7.0  และ .NET Core  2.0 ที่เกี่ยวข้องกับการสื่อสารข้อมูลในเครือข่าย เช่นClass Socket การสร้าง object แบบSocket และการสื่อสารแบบไร้การเชื่อมต่อ ทั้งนี้ท่านผู้อ่านน่าจะได้ทดลองใช้ประโยชน์จาก Class Socket ดูนะครับ