แนะนำ WebSockets - นำซ็อกเก็ตขึ้นไปบนเว็บ

ปัญหา: การเชื่อมต่อแบบเซิร์ฟเวอร์กับไคลเอ็นต์และแบบไคลเอ็นต์กับเซิร์ฟเวอร์ที่มีเวลาในการตอบสนองต่ำ

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

เทคโนโลยีที่ช่วยให้เซิร์ฟเวอร์ส่งข้อมูลไปยังไคลเอ็นต์ได้ทันทีที่ทราบว่ามีข้อมูลใหม่นั้นมีอยู่มาระยะหนึ่งแล้ว ซึ่งอาจตั้งเป็นชื่อต่างๆ เช่น "Push" หรือ "Comet" หนึ่งในแฮ็กที่พบบ่อยที่สุดในการสร้างภาพลวงว่าเซิร์ฟเวอร์เป็นผู้เริ่มการเชื่อมต่อเรียกว่าการโพลลิงแบบนาน เมื่อใช้การโพลลิงแบบนาน ไคลเอ็นต์จะเปิดการเชื่อมต่อ HTTP กับเซิร์ฟเวอร์ซึ่งจะเปิดอยู่จนกว่าจะส่งการตอบกลับ เมื่อใดก็ตามที่เซิร์ฟเวอร์มีข้อมูลใหม่จริง ก็จะส่งการตอบกลับ (เทคนิคอื่นๆ เกี่ยวข้องกับคำขอ Flash, XHR multipart และสิ่งที่เรียกว่า htmlfiles) การโหวตแบบ Long Polling และเทคนิคอื่นๆ ได้ผลดีทีเดียว คุณใช้อีโมจิทุกวันในแอปพลิเคชันต่างๆ เช่น แชทใน Gmail

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

ขอแนะนํา WebSocket: การนำซ็อกเก็ตมาสู่เว็บ

ข้อกำหนด WebSocket กำหนด API ที่สร้างการเชื่อมต่อ "ซ็อกเก็ต" ระหว่างเว็บเบราว์เซอร์กับเซิร์ฟเวอร์ กล่าวอย่างง่ายคือ มีการเชื่อมต่อแบบถาวรระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์ และทั้ง 2 ฝ่ายสามารถเริ่มส่งข้อมูลได้ทุกเมื่อ

เริ่มต้นใช้งาน

คุณเปิดการเชื่อมต่อ WebSocket ได้ง่ายๆ เพียงเรียกคอนสตรัคเตอร์ WebSocket ดังนี้

var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);

โปรดสังเกต ws: นี่คือสคีมา URL ใหม่สำหรับการเชื่อมต่อ WebSocket นอกจากนี้ยังมีwss: สำหรับการเชื่อมต่อ WebSocket ที่ปลอดภัยด้วย เช่นเดียวกับการใช้ https: สำหรับการเชื่อมต่อ HTTP ที่ปลอดภัย

การแนบตัวแฮนเดิลเหตุการณ์บางรายการกับการเชื่อมต่อทันทีจะช่วยให้คุณทราบว่าการเชื่อมต่อเปิดอยู่ ได้รับข้อความขาเข้า หรือมีข้อผิดพลาด

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

ชื่อโปรโตคอลย่อยต้องเป็นหนึ่งในชื่อโปรโตคอลย่อยที่จดทะเบียนในรีจิสทรีของ IANA ปัจจุบันมีชื่อโปรโตคอลย่อย (Soap) เพียงชื่อเดียวที่จดทะเบียนตั้งแต่เดือนกุมภาพันธ์ 2012

// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};

// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};

// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};

การสื่อสารกับเซิร์ฟเวอร์

ทันทีที่เรามีการเชื่อมต่อกับเซิร์ฟเวอร์ (เมื่อมีการเรียกเหตุการณ์ open) เราจะเริ่มส่งข้อมูลไปยังเซิร์ฟเวอร์ได้โดยใช้เมธอด send('your message') ในออบเจ็กต์การเชื่อมต่อ ตัวเลือกนี้เคยรองรับสตริงเท่านั้น แต่ในข้อกำหนดล่าสุด ตอนนี้สามารถส่งข้อความไบนารีได้ด้วย หากต้องการส่งข้อมูลไบนารี คุณสามารถใช้ออบเจ็กต์ Blob หรือ ArrayBuffer ก็ได้

// Sending String
connection.send('your message');

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);

// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);

ในขณะเดียวกัน เซิร์ฟเวอร์อาจส่งข้อความถึงเราได้ทุกเมื่อ เมื่อเกิดเหตุการณ์นี้ขึ้น ระบบจะเรียกใช้การเรียกกลับ onmessage Callback จะได้รับออบเจ็กต์เหตุการณ์และจะเข้าถึงข้อความจริงได้ผ่านพร็อพเพอร์ตี้ data

WebSocket ยังรับข้อความไบนารีในข้อกำหนดล่าสุดได้ด้วย โดยจะรับเฟรมไบนารีในรูปแบบ Blob หรือ ArrayBuffer หากต้องการระบุรูปแบบของไบนารีที่ได้รับ ให้ตั้งค่าพร็อพเพอร์ตี้ binaryType ของออบเจ็กต์ WebSocket เป็น "blob" หรือ "arraybuffer" รูปแบบเริ่มต้นคือ "blob" (คุณไม่จำเป็นต้องจัดแนวพารามิเตอร์ binaryType เมื่อส่ง)

// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};

ฟีเจอร์ใหม่ที่เพิ่มเข้ามาอีกอย่างหนึ่งของ WebSocket คือส่วนขยาย การใช้ส่วนขยายจะช่วยให้สามารถส่งเฟรมที่บีบอัด มัลติเพล็กซ์ และอื่นๆ ได้ คุณดูส่วนขยายที่เซิร์ฟเวอร์ยอมรับได้โดยตรวจสอบพร็อพเพอร์ตี้ extensions ของออบเจ็กต์ WebSocket หลังจากเหตุการณ์เปิด ยังไม่มีข้อกําหนดเฉพาะของส่วนขยายที่เผยแพร่อย่างเป็นทางการ ณ เดือนกุมภาพันธ์ 2012

// Determining accepted extensions
console.log(connection.extensions);

การสื่อสารข้ามแหล่งที่มา

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

พร็อกซีเซิร์ฟเวอร์

เทคโนโลยีใหม่ทุกเทคโนโลยีมาพร้อมกับปัญหาชุดใหม่ ในกรณีของ WebSocket นั้น จะเป็นความเข้ากันได้กับพร็อกซีเซิร์ฟเวอร์ที่ทำหน้าที่เป็นสื่อกลางในการเชื่อมต่อ HTTP ในเครือข่ายของบริษัทส่วนใหญ่ โปรโตคอล WebSocket ใช้ระบบการอัปเกรด HTTP (ซึ่งปกติจะใช้สำหรับ HTTP/SSL) เพื่อ "อัปเกรด" การเชื่อมต่อ HTTP เป็นการเชื่อมต่อ WebSocket บางพร็อกซีเซิร์ฟเวอร์ไม่ชอบสิ่งนี้และจะตัดการเชื่อมต่อ ดังนั้น แม้ว่าไคลเอ็นต์จะใช้โปรโตคอล WebSocket ก็ตาม แต่อาจสร้างการเชื่อมต่อไม่ได้ ด้วยเหตุนี้ ส่วนถัดไปจึงมีความสำคัญมากยิ่งขึ้นไปอีก :)

ใช้ WebSockets วันนี้

WebSocket ยังเป็นเทคโนโลยีใหม่และยังไม่ติดตั้งใช้งานในเบราว์เซอร์บางรุ่น อย่างไรก็ตาม คุณสามารถใช้ WebSocket กับไลบรารีที่ใช้ทางเลือกใดทางเลือกหนึ่งข้างต้นได้ทันทีเมื่อ WebSocket ไม่พร้อมใช้งาน ไลบรารีที่ได้รับความนิยมอย่างมากในโดเมนนี้คือ socket.io ซึ่งมาพร้อมกับไคลเอ็นต์และเซิร์ฟเวอร์ที่ใช้โปรโตคอล รวมถึงมีทางเลือกสำรอง (socket.io ยังไม่รองรับการรับส่งข้อความไบนารีตั้งแต่เดือนกุมภาพันธ์ 2012) นอกจากนี้ ยังมีโซลูชันเชิงพาณิชย์ เช่น PusherApp ซึ่งสามารถผสานรวมเข้ากับสภาพแวดล้อมแบบเว็บใดก็ได้อย่างง่ายดาย โดยการให้ HTTP API เพื่อส่งข้อความ WebSocket ไปยังไคลเอ็นต์ เนื่องจากมีคำขอ HTTP เพิ่มเติม จึงจะมีค่าใช้จ่ายเพิ่มเติมเมื่อเทียบกับ WebSocket ล้วนๆ

ฝั่งเซิร์ฟเวอร์

การใช้ WebSocket จะสร้างรูปแบบการใช้งานแบบใหม่สําหรับแอปพลิเคชันฝั่งเซิร์ฟเวอร์ แม้ว่าสแต็กเซิร์ฟเวอร์แบบดั้งเดิม เช่น LAMP จะออกแบบมาเพื่อจัดการกับวงจรคำขอ/การตอบกลับ HTTP แต่มักจัดการกับการเชื่อมต่อ WebSocket ที่เปิดอยู่จำนวนมากได้ไม่ดีนัก การเปิดการเชื่อมต่อจำนวนมากไว้พร้อมกันต้องใช้สถาปัตยกรรมที่รองรับการทำงานพร้อมกันได้สูงโดยมีต้นทุนด้านประสิทธิภาพต่ำ โดยปกติแล้ว สถาปัตยกรรมดังกล่าวออกแบบมาเพื่อใช้การแยกชุดข้อความหรือที่เรียกว่า IO แบบไม่บล็อก

การใช้งานฝั่งเซิร์ฟเวอร์

เวอร์ชันของโปรโตคอล

โปรโตคอลการเดินสาย (แฮนด์เชคและการโอนข้อมูลระหว่างไคลเอ็นต์กับเซิร์ฟเวอร์) สำหรับ WebSocket เปลี่ยนเป็น RFC6455 แล้ว Chrome และ Chrome สำหรับ Android เวอร์ชันล่าสุดใช้งานร่วมกับ RFC6455 รวมถึงการรับส่งข้อมูลไบนารีได้อย่างเต็มที่ นอกจากนี้ Firefox จะใช้งานร่วมกับเวอร์ชัน 11 ได้ ส่วน Internet Explorer จะใช้งานร่วมกับเวอร์ชัน 10 ได้ คุณยังคงใช้โปรโตคอลเวอร์ชันเก่าได้ แต่ไม่แนะนำเนื่องจากเป็นที่ทราบกันว่าโปรโตคอลเวอร์ชันเก่ามีช่องโหว่ หากคุณมีการใช้งานเซิร์ฟเวอร์สำหรับโปรโตคอล WebSocket เวอร์ชันเก่า เราขอแนะนำให้อัปเกรดเป็นเวอร์ชันล่าสุด

กรณีการใช้งาน

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

  • เกมออนไลน์แบบผู้เล่นหลายคน
  • แอปพลิเคชัน Chat
  • ทิกเกอร์การแข่งขันกีฬาแบบสด
  • การอัปเดตสตรีมโซเชียลแบบเรียลไทม์

เดโม

ข้อมูลอ้างอิง