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

Creating a Connection

ทุกอย่างเริ่มต้นด้วยบรรทัดเดียว constructor คือจุดที่ WebSocket ถือกำเนิด และการตัดสินใจไม่กี่อย่างที่คุณทำตรงนี้ — เลือกใช้ scheme ไหน, subprotocol ไหน — เป็นตัวกำหนดรูปร่างของการเชื่อมต่อทั้งหมด มาแยกบรรทัดนั้นออกดูทีละส่วนกัน

const ws = new WebSocket(url);
// or, asking for a specific application protocol:
const ws = new WebSocket(url, protocols);

อาร์กิวเมนต์แรก url จำเป็นต้องมี และต้องเป็น URL แบบสมบูรณ์ที่ใช้ WebSocket scheme ส่วนอาร์กิวเมนต์ที่สอง protocols เป็นทางเลือก: เป็นสตริงเดียวหรืออาร์เรย์ของสตริงที่ระบุ subprotocol ที่ client ของคุณยินดีจะพูด นั่นคือลายเซ็นทั้งหมด — URL บังคับหนึ่งตัวกับลิสต์ทางเลือกหนึ่งตัว

WebSocket URL ไม่เคยใช้ http แต่ใช้หนึ่งในสอง scheme เฉพาะทาง:

  • ws:// — การเชื่อมต่อแบบไม่เข้ารหัส ไบต์เดินทางแบบเปิดเผย เหมือน http:// ธรรมดาทุกประการ
  • wss:// — โปรโตคอลเดียวกันที่ห่อด้วย TLS เหมือน https:// ทุกประการ เข้ารหัส ยืนยันตัวตน และแก้ไขดัดแปลงได้ยากกว่ามาก

กฎในทางปฏิบัติเรียบง่าย: ใช้ wss:// สำหรับทุกอย่างที่ไม่ใช่การทดสอบในเครื่องแบบใช้แล้วทิ้ง เบราว์เซอร์ยังบังคับใช้กฎ mixed-content อีกด้วย — หน้าเว็บที่เสิร์ฟผ่าน https:// ไม่ได้รับอนุญาตให้เปิดการเชื่อมต่อ ws:// ธรรมดา ดังนั้นบนเว็บไซต์จริงทุกแห่ง wss:// จึงแทบเป็นข้อบังคับ

const dev = new WebSocket('ws://localhost:8080'); // fine for local dev only
const prod = new WebSocket('wss://chat.example.com'); // always this in production

argument ตัวที่สองซึ่งใส่หรือไม่ใส่ก็ได้ ช่วยให้ client กับ server ตกลงกันว่า จะพูด protocol ระดับ application ตัวไหน บน socket — เหมือนเลือกภาษากลางก่อนเริ่มบทสนทนา เบราว์เซอร์ส่งลิสต์ของคุณไป server เลือกมา หนึ่งตัว แล้วตัวที่เลือกจะกลับมาอยู่ใน ws.protocol ตอนการเชื่อมต่อเปิด

const ws = new WebSocket('wss://example.com', ['chat.v2', 'chat.v1']);
ws.onopen = () => {
console.log('server chose:', ws.protocol); // e.g. "chat.v2"
};

คุณเรียงลิสต์ตามลำดับความชอบ และ server ควรเคารพลำดับนั้น ถ้า server ไม่รองรับสักตัว ก็ควรปฏิเสธการเชื่อมต่อไปเลย subprotocol จะใส่หรือไม่ใส่ก็ได้ — หลายแอปไม่เคยใช้เลย — แต่เป็นวิธีที่สะอาดในการกำหนดเวอร์ชันของรูปแบบข้อมูลบนสาย

การเรียก new WebSocket(url) ไม่ บล็อก แต่คืนค่าทันทีโดย object อยู่ในสถานะ CONNECTING ระหว่างที่ handshake ทำงานอยู่เบื้องหลัง ไดอะแกรมนี้ตามรอยช่วงเวลาแรก ๆ เหล่านั้น:

sequenceDiagram
  participant App as Your code
  participant WS as WebSocket object
  participant Srv as Server
  App->>WS: new WebSocket(url, protocols)
  Note over WS: readyState = CONNECTING (returns immediately)
  WS->>Srv: HTTP Upgrade request (+ chosen protocols)
  Srv-->>WS: 101 Switching Protocols (picks one protocol)
  Note over WS: readyState = OPEN
  WS-->>App: onopen fires
  App->>WS: now safe to ws.send(...)
จากการสร้างจนถึง open

เพราะการสร้างคืนค่าทันที ออบเจกต์จึงยังไม่พร้อมส่งอะไรเลย — readyState ยังเป็น CONNECTING อยู่ คุณต้องผูก handler ทันทีหลังจากสร้าง แล้วรอ onopen การพยายาม send() ก่อนหน้านั้นจะ throw เราจะอาศัยหลักการนี้ในบทเรียนถัดไป

เดโมใช้ echo socket ในหน้าเว็บที่มี WebSocket API จริง จึงรันได้ทุกที่โดยไม่ต้องมี server ตัวเดโมเลียนแบบ constructor แบบสอง argument และรายงาน protocol ที่เจรจาได้ ในแอปจริงคุณจะเขียน new WebSocket('wss://your-server', ['chat.v2']) และไม่มีอะไรอื่นเปลี่ยน

JavaScript

ล็อกแรกพิสูจน์ว่าการสร้างคืนค่ามาโดย readyState ยังเป็น 0 อยู่ ต่อมาภายใน onopen เท่านั้นการเชื่อมต่อจึงใช้งานได้ — และ subprotocol ที่เจรจาได้ก็พร้อมใช้ตรงนั้นในรูปของ ws.protocol

ข้อดี (Browser WebSocket API)ข้อแลกเปลี่ยน
built-in browser API — ไม่ต้อง install libraryAPI เรียบง่าย แต่ไม่มี reconnect built-in
ทำงานบน HTTP/HTTPS port เดิม (80/443)ไม่มี message queue — ส่งขณะ connecting fail
ws:// และ wss:// — TLS ใน URLไม่มี timeout built-in — ต้อง implement เอง
event-based — ไม่ block main threadsend() ขณะ readyState ไม่ใช่ OPEN throw error

ส่ง Message ก่อน Connection OPEN อาการ:

  • ws.send(data) ทันทีหลัง new WebSocket(url) — ยังไม่ OPEN
  • InvalidStateError throw — message หาย
  • ส่งใน ws.onopen เสมอ หรือ queue message แล้วส่งเมื่อ open

ไม่ Handle Connection Error อาการ:

  • ไม่มี ws.onerror handler
  • connection fail โดยไม่รู้สาเหตุ — UX แย่
  • implement onerror และ onclose ที่ log error และ trigger reconnect

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

Discord Web App:

  • new WebSocket('wss://gateway.discord.gg/?v=10') ตอน app load
  • หาก connect fail → retry ด้วย exponential backoff

Binance Web Trading:

  • connect ไปยัง wss://stream.binance.com:9443/stream
  • reconnect อัตโนมัติเมื่อ connection drop — trader ไม่ miss price update
WebSocket ในโปรดักชันควรใช้ URL scheme แบบใด?
จุดประสงค์ของอาร์กิวเมนต์ตัวที่สองที่เป็นทางเลือกของ constructor คืออะไร?
object อยู่ในสถานะใดทันทีหลังจาก new WebSocket(url) คืนค่า?