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

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

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

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

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

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

ข้อกำหนดของ WebSocket กำหนด API ที่สร้างการเชื่อมต่อ "Socket" ระหว่างเว็บเบราว์เซอร์และเซิร์ฟเวอร์ พูดง่ายๆ ก็คือ มีการเชื่อมต่ออย่างต่อเนื่องระหว่างไคลเอ็นต์และเซิร์ฟเวอร์ และทั้ง 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);

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

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

// 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 คือส่วนขยาย เมื่อใช้ส่วนขยาย คุณจะส่งเฟรมที่บีบอัด มัลติเพล็กซ์ ฯลฯ ได้ คุณสามารถค้นหาส่วนขยายที่เซิร์ฟเวอร์ยอมรับได้โดยตรวจสอบพร็อพเพอร์ตี้ส่วนขยายของออบเจ็กต์ 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 ยังทำงานร่วมกับ Internet Explorer เวอร์ชัน 10 ในเวอร์ชัน 11 ได้ด้วย คุณจะยังใช้โปรโตคอลเวอร์ชันเก่าได้ แต่ไม่แนะนำให้ใช้เนื่องจากเป็นที่ทราบกันว่ามีช่องโหว่ หากคุณใช้เซิร์ฟเวอร์สำหรับโปรโตคอล WebSocket เวอร์ชันเก่า เราขอแนะนำให้คุณอัปเกรดเป็นเวอร์ชันล่าสุด

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

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

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

เดโม

รายการอ้างอิง