ข้ามไปยังเนื้อหา

WebSocket คืออะไร?

ในบทเรียนแรกเราบอกว่า WebSocket คือ การเชื่อมต่อแบบ persistent และ full-duplex เส้นเดียว บทเรียนนี้จะค่อย ๆ เจาะว่า ทำไมประโยคเดียวนี้ถึงเปลี่ยนวิธีที่คุณสร้างซอฟต์แวร์ — พร้อมให้ลองสัมผัสจริงด้วยเดโมสดอีกอัน

ลองนึกภาพว่าเบราว์เซอร์คุยกับ server ตามปกติอย่างไร ทุกการโต้ตอบคือธุระแยกกันที่จบในตัวเอง:

  • เปิดการเชื่อมต่อ (หรือยืมจาก pool ที่มีอยู่)
  • ส่ง request
  • รับ response
  • การแลกเปลี่ยนจบลง server ลืมคุณไป

ถ้าหน้าหนึ่งต้องการข้อมูลสิบชิ้น นั่นก็คือสิบธุระในเชิงแนวคิด แต่ละธุระแบก header ของตัวเอง แบกต้นทุนการตั้งต้นของตัวเอง และ — ที่สำคัญ — ไหลไปในทิศทางที่ client เป็นคนเริ่มเท่านั้น server พูดก่อนไม่ได้ ทำได้แค่ตอบ

WebSocket แทนที่รูปแบบทั้งหมดนั้นด้วยสายเปิดเส้นเดียว:

flowchart TB
  subgraph http["HTTP — many short exchanges"]
    direction LR
    c1["client"] -- "req 1" --> s1["server"]
    s1 -- "res 1" --> c1
    c2["client"] -- "req 2" --> s2["server"]
    s2 -- "res 2" --> c2
    c3["client"] -- "req 3" --> s3["server"]
    s3 -- "res 3" --> c3
  end
  subgraph ws["WebSocket — one persistent connection"]
    direction LR
    cw["client"] <--> sw["server"]
  end
การแลกเปลี่ยน HTTP สั้น ๆ หลายครั้ง เทียบกับ WebSocket แบบ persistent เส้นเดียว

เมื่อสายนั้นเปิดแล้ว การส่งข้อความก็ถูก: ไม่มีการเชื่อมต่อใหม่ให้ต้องเจรจา และไม่มี header overhead ต่อ request และเพราะสายนั้นเป็นสองทางอย่างแท้จริง server จึงส่งข้อความได้ทันทีที่มีอะไรเกิดขึ้น — บรรทัดแชตใหม่ ราคาที่เปลี่ยนไป การแก้ไขของผู้ใช้คนอื่น — โดยที่ client ไม่เคยต้องร้องขอเลย

จะเห็นภาพชัดขึ้นถ้าวางทั้งสองแบบเทียบกันเป็นร้อยแก้วธรรมดาแทนไดอะแกรม:

  • HTTP request/response คือ client เป็นผู้เริ่มและจบในครั้งเดียว client ถาม server ตอบ จบ การจะรู้เรื่องเหตุการณ์ใหม่ client ต้องถามอีกครั้ง
  • WebSocket คือ ฝ่ายใดฝ่ายหนึ่งเริ่มได้และต่อเนื่อง หลังการตั้งต้นครั้งเดียว ทั้งสองปลายถือการเชื่อมต่อเดิมเปิดค้างไว้ แล้ว push message ข้ามไปมาได้นานเท่าที่ต้องการ

การเปลี่ยนผ่านนั้น — จาก “ถามซ้ำแล้วซ้ำเล่า” ไปสู่ “เชื่อมต่อค้างไว้แล้วฟัง” — คือคุณค่าทั้งหมดที่นำเสนอ

WebSocket คือเครื่องมือที่ถูกต้องเมื่อใดก็ตามที่ server ต้อง push ข้อมูลที่ client ไม่อาจคาดเดาล่วงหน้าได้:

  • แชตและการส่งข้อความ — ข้อความจากผู้ใช้คนหนึ่งต้องไปถึงคนอื่นทันที โดยไม่ต้อง polling
  • ฟีดข้อมูลสด — ราคา คะแนน ค่าจากเซ็นเซอร์ แดชบอร์ดที่อัปเดตตามโลกที่เปลี่ยนไป
  • การทำงานร่วมกัน — เอกสารที่แชร์กัน ไวต์บอร์ด และเครื่องมือออกแบบที่ทุกการกดแป้นหรือการเลื่อนเคอร์เซอร์ส่งต่อไปยังทุกคน
  • การแจ้งเตือนและสถานะการมีอยู่ — “มีออเดอร์ใหม่เข้ามา”, “Sam กำลังพิมพ์”, “บิลด์ของคุณเสร็จแล้ว”
  • มัลติเพลเยอร์และเกม — การอัปเดตสถานะที่ latency ต่ำ พุ่งไปมาทั้งสองทางหลายครั้งต่อวินาที

เส้นด้ายร่วมกัน: เหตุการณ์เกิดขึ้นที่ฝั่ง server ในเวลาที่คาดเดาไม่ได้ และ latency มีความสำคัญ นั่นคือช่องว่างที่การเชื่อมต่อแบบ persistent และ bidirectional เข้ามาเติมเต็มพอดี

ลองรันเดโมข้างล่าง เดโมใช้ in-page echo socket ที่มี WebSocket API ของจริง — มี onopen/onmessage/send/close ครบ — จึงรันได้ทุกที่โดยไม่ต้องมี server เดโมส่งไปหนึ่งบรรทัดแล้วพิมพ์สิ่งที่ตอบกลับมา สังเกตว่าหลัง open เพียงครั้งเดียว การเชื่อมต่อก็แค่ นั่งพร้อมอยู่ตรงนั้น — คุณส่งอีกได้ทุกเมื่อโดยไม่ต้องเชื่อมต่อใหม่

JavaScript

บรรทัด received: แรกคือคำทักทายของ server ตอนเชื่อมต่อ ส่วนบรรทัดที่สองคือข้อความของเราเองที่ถูก echo กลับมา เราปิดก็ต่อเมื่อข้อความที่สองมาแล้วเท่านั้น — เป็นการพิสูจน์ว่าการเชื่อมต่อเปิดค้างและใช้งานได้ตลอดเวลา นั่นคือสิ่งที่ HTTP request/response ให้ไม่ได้เลย

ข้อดี (WebSocket)ข้อแลกเปลี่ยน
latency ต่ำมาก — ไม่มี HTTP overhead ต่อ messageserver ต้องเก็บ state ต่อ connection — ไม่ stateless เหมือน HTTP
bidirectional — server push ได้ทันทีconnection ที่เปิดค้างกิน memory ต่อ user
ไม่มี polling overhead — ส่งเฉพาะเมื่อมี eventscaling ยากกว่า REST API — ต้องจัดการ sticky session
ลด HTTP request — ประหยัด bandwidthไม่ cacheable — CDN ช่วยไม่ได้

ใช้ WebSocket สำหรับ Operation ที่ HTTP เหมาะกว่า อาการ:

  • ใช้ WebSocket สำหรับ form submit, file upload, data fetch ครั้งเดียว
  • complexity สูงโดยไม่ได้ benefit จาก real-time
  • ใช้ WebSocket เฉพาะเมื่อต้องการ server push หรือ bidirectional low-latency

ไม่มี Reconnect Logic อาการ:

  • connection ขาด → app หยุดทำงานโดยไม่แจ้ง user
  • user ต้อง refresh หน้าเพื่อเชื่อมต่อใหม่
  • implement onclose handler ที่ reconnect อัตโนมัติด้วย exponential backoff

💡 ตัวอย่างจากของจริง

Discord:

  • WebSocket สำหรับ real-time message, presence, voice state
  • fallback ไปยัง long polling เมื่อ WebSocket ไม่ available

Slack:

  • RTM (Real Time Messaging) API ผ่าน WebSocket
  • ทุก message, reaction, typing indicator ส่งผ่าน WebSocket connection เดียว
อะไรคือสิ่งที่แยก WebSocket ออกจาก HTTP request/response อย่างเป็นรากฐานที่สุด?
สถานการณ์ใดที่เหมาะกับ WebSocket มากที่สุด?
หลังจากเดโมพิมพ์บรรทัด "received" แรก ทำไมถึงส่งหรือปิดได้โดยไม่ต้องเชื่อมต่อใหม่?