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


แต่ Web Socket เปรียบเสมือนต่อโทรศัพท์โทรไปแล้วคาสายไว้ อีกฝั่งเรียกเมื่อไหร่ก็ได้ยิน ตอบกลับได้ทันที

เพื่อให้เข้าใจแบบถ่องแทร่ !!! เรามาทำความเข้าใจที่มาของ Web Socket กันดีกว่า

หากย้อนไปในเว็บไซต์ในยุค 90 ในช่วงปี 1990 อินเทอร์เน็ตกลายเป็นแหล่งข้อมูลสำคัญ เว็บไซต์มีเยอะขึ้น คนใช้เว็บเยอะขึ้น และเว็บเบราว์เซอร์ก็เริ่มมีฟีเจอร์เจ๋งๆเพิ่มมาเรื่อยๆ เริ่มมีการบิด HTTP มาโมดิฟายให้สามารถรับส่งข้อมูลได้แบบทันทีสมัยนั้นจะเป็น HTTP 0.9 บิดไปบิดมาก็ไป HTTP 0.9 เวอร์ชันต่าง ๆ มากมายไม่มีมาตรฐาน แต่การใช้ HTTP มันยังโต้ตอบได้ไม่ทันใจ ในปี 2008 นักเดฟรุ่นเก๋าอย่าง Michael Carter กับ Ian Hickson รู้สึกปวดหัวมากกับการใช้ Comet (เทคนิคเก่าๆ เดี๋ยวมีอธิบายด้านล่างงับ) เวลาอยากทำให้เว็บอัปเดตทันที พวกเหล่าเดฟรุ่นเก๋าเหล่านี้เลยไปคุยกันผ่านห้อง Chat (IRC) และในเว็บบอร์ดของ W3C จนได้ไอเดียว่าต้องสร้างมาตรฐานใหม่สำหรับการสื่อสารแบบทันทีบนเว็บจริงๆ และแล้วชื่อ “WebSocket” ก็ถือกำเนิดขึ้น
แวะมาอธิบาย Comet: ก่อนจะมี WebSockets…
- Comet เปิดตัวในปี 2006 เป็นแนวทางการสร้างเว็บแอปที่ให้ตัวเว็บเซิร์ฟเวอร์ดันข้อมูลหาเบราว์เซอร์ได้โดยผู้ใช้ไม่ต้องร้องขอ
- คล้ายกับ AJAX ที่ทำให้เว็บคุยกันแบบไม่ต้องรีเฟรชหน้า แต่ Comet ไม่เหมือน AJAX ตรงที่ AJAX แบบเดิมเน้นให้ผู้ใช้เรียกดึงข้อมูลจากเซิร์ฟเวอร์อย่างเดียว
- Comet ใช้การเชื่อมต่อ HTTP แบบทิ้งไว้นานๆ ให้เซิร์ฟเวอร์สามารถดันอัปเดตไปให้ได้ทันทีที่มีข้อมูล โดยที่ผู้ใช้ไม่ต้องทำอะไร
- Comet ดังมากๆเพราะทั้ง Google และ Meebo ใช้เทคนิคนี้ (Google เอาไปสร้าง web-based chat ใน Gmail ส่วน Meebo ใช้ทำโปรแกรมแชทจากหลายๆบริการรวมในหน้าเว็บ)
- ทำให้ยุคนึง Comet กลายเป็นวิธีมาตรฐานในการทำเว็บที่ต้องการการตอบสนองฉับไว
เทคนิคในการทำ Comet
มีหลายวิธี แต่วิธีเด่นๆคือ:
- Long polling: เบราว์เซอร์จะเปิดการเชื่อมต่อไปยังเซิร์ฟเวอร์ค้างไว้ ถ้าเซิร์ฟเวอร์ไม่มีข้อมูล ก็ปิดการเชื่อมต่อแล้วเปิดใหม่ทันที ทำวนแบบนี้ไปเรื่อยๆ พอเซิร์ฟเวอร์มีข้อมูลใหม่ก็ส่งไปเลย

- HTTP streaming: คล้ายกับ Long polling แต่ไม่ต้องคอยปิดแล้วเปิดการเชื่อมต่อใหม่ๆ ส่งข้อมูลไปเรื่อยๆเลยไม่หยุด
AJAX กับ Comet ช่วยให้เว็บตอบสนองได้ทันที…แต่มีข้อเสีย
- ถึงแม้ AJAX และ Comet จะยังมีใช้อยู่บ้าง แต่ทั้งคู่มีปัญหาอยู่เยอะ เพราะพื้นฐานคือไปบิด HTTP ให้ทำงานเป็นแบบที่มันไม่ได้ถูกสร้างมาแต่แรก
- HTTP ถูกสร้างมาเพื่อ response ต่อ request ที่เข้ามาเป็นครั้งๆไป ไม่ได้เน้นตอบสนองทันทีแบบแอปยุคนี้ที่ต้องอัปเดตตลอดๆ
ปัญหาหลักๆ ของการดันทุรังใช้ HTTP มีดังนี้ (มีปัญหาอื่นอีก แต่ขอยกแค่ตัวอย่างเด่นๆ)
- จำกัดจำนวนคนใช้ได้: ยกตัวอย่าง HTTP polling คือต้องส่งคำขอไปหาเว็บเซิร์ฟเวอร์เป็นๆระยะเพื่อถามว่ามีอัปเดตใหม่ไหม? ถ้าถามบ่อยก็เปลืองทั้งเน็ตและเปลืองทรัพยากรเซิร์ฟเวอร์มาก…พอคนใช้เยอะๆระบบก็รวน
- รับรองความต่อเนื่องไม่ได้: HTTP ไม่ได้ออกแบบมาให้มั่นใจว่าจะส่งข้อมูลถึงแน่ๆ หรือถึงตามลำดับที่ส่งไปจากผู้ใช้ บางทีอาจหลุด หรือลำดับมั่วได้
- เปลือง resource: โดยเฉพาะ long polling ที่เปิดการเชื่อมต่อทิ้งไว้…ยิ่งคนเชื่อมต่อเยอะ ก็ยิ่งเปลือง
ปัญหาพวกนี้แหละเลยก่อให้เกิด WebSockets นั่นเอง เพราะต้องการแก้ปัญหาโดยออกแบบวิธีสื่อสารโดยเฉพาะ แทนที่จะฝืนใช้ HTTP ต่อไป
สรุปแล้ว WebSocket คือ…
- เทคโนโลยีที่ทำให้เว็บสื่อสารได้สองทางเต็มๆ (client-server) ผ่านการเชื่อมต่อแค่ครั้งเดียว แถมยาวๆ ไม่ต้องมานั่งเชื่อมต่อใหม่บ่อยๆ
- เป้าหมายคือทำให้การพัฒนาแอปบนเว็บมีวิธีคุยกับเซิร์ฟเวอร์เหมือนการคุยบนเครือข่ายพื้นฐาน (TCP) ได้โดยตรงเลย แต่ก็มีปรับแต่งบางอย่างให้เข้ากับการทำงานของเว็บไซต์ให้ลื่นที่สุด
- เวลาเชื่อมต่อด้วย WebSocket จะเริ่มเป็นคำขอตามปกติแบบ HTTP ก่อน จากนั้นพอคุยกันรู้เรื่องแล้วจะเปลี่ยนโปรโตคอลการสื่อสารไปเลย เท่ากับ HTTP กับ WebSocket ต่างกันแบบแทบไม่เหลือเค้าโครงเดิมเลย
WebSocket ประกอบด้วย 2 ส่วนสำคัญ
- WebSocket Protocol: ตัวโปรโตคอลการคุยกันบนเว็บ ให้ส่งได้ทั้งข้อมูลตัวอักษรหรือข้อมูลดิบๆ (พวกเลขหรือสัญญาณต่างๆ ที่ไม่ใช่อักษร)
- WebSocket API: ชุดตัวช่วยในการจัดการการเชื่อมต่อ WebSocket ทั้งสร้างการเชื่อมต่อ ส่ง-รับข้อความ หรือดักฟังคำสั่งจากเซิร์ฟเวอร์
WebSockets vs HTTP

การสื่อสาร
WebSocket: Full duplex สองทางเต็มระบบ
HTTP: Half duplex ขาเดียว (ต้องร้องขอแล้วค่อยได้คำตอบ)
รูปแบบการแลกเปลี่ยนข้อความ
WebSocket: Bidirectional สองทาง (ส่งและรับได้อิสระ)
HTTP: Request – response ตามคำขอเท่านั้น
Server push
WebSocket: ทำได้เลย
HTTP: ทำไม่ได้โดยตรง
Overhead
WebSocket: ต่ำ
HTTP: สูงกว่า
Stateful
WebSocket: Stateful จำได้ว่าคุยถึงไหน
HTTP: Stateless ต้องเริ่มต้นใหม่ตลอด
สรุปคือ HTTP กับ WebSockets เหมาะกับงานคนละแบบ
- HTTP เหมาะกับแอปที่เน้นการดึง/เพิ่ม/แก้ไข/ลบข้อมูล และไม่ต้องการให้มีการอัปเดตทันทีทันใด
- WebSockets เหมาะสำหรับแอปที่ต้องการความเร็ว การตอบสนองแบบฉับไว และปรับตัวได้ดีกับจำนวนผู้ใช้เยอะๆ
โดยในปี 2011 องค์กรกำหนดมาตรฐานด้านอินเทอร์เน็ตอย่าง IETF ได้ทำให้ WebSocket Protocol เป็นมาตรฐานผ่านเอกสาร RFC 6455 และในสาระสำคัญของ WebSocket Protocol (ตาม RFC 6455) คือ

เริ่มต้นจากการเชื่อมต่อแบบ HTTP ก่อน จากนั้นจะอัพเกรดเป็นการสื่อสารแบบ WebSockets ข้อมูลที่ส่งผ่าน WebSockets จะมาในรูปของ “ข้อความ” ซึ่งภายในหนึ่งข้อความอาจมีข้อมูลถูกแบ่งเป็นหลายๆส่วนที่เรียกว่า “เฟรม” ก็ได้ ทั้งผู้ใช้และเว็บเซิร์ฟเวอร์สามารถส่งข้อมูลหาได้ตลอดเวลาหากยังเชื่อมต่อกันอยู่ สำหรับตอนปิดการเชื่อมต่อที่เป็นไปตามมาตรฐานเรียกว่า “closing handshake”
ตัวอย่างการใช้งาน Web Socket ร่วมกับ Node.js
const WebSocket = require('ws');
const port = 8080; // Choose your desired port
const server = new WebSocket.Server({ port });
server.on('connection', (websocket) => {
console.log('Client connected');
websocket.on('message', (message) => {
console.log(`Received message: ${message}`);
server.clients.forEach((client) => {
if (client !== websocket && client.readyState === WebSocket.OPEN) {
client.send(message); // Broadcast to other clients
}
});
});
websocket.on('close', () => {
console.log('Client disconnected');
});
});
console.log(`WebSocket server listening on port ${port}`);
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Example</title>
</head>
<body>
<input type="text" id="messageInput" />
<button onclick="sendMessage()">Send</button>
<div id="messages"></div>
<script src="client.js"></script>
</body>
</html>
const webSocket = new WebSocket('ws://localhost:8080'); // Adjust if port is different
webSocket.onopen = () => {
console.log('WebSocket connection established');
};
webSocket.onmessage = (event) => {
const messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML += `Received message: ${event.data}<br>`;
};
function sendMessage() {
const messageInput = document.getElementById('messageInput');
const message = messageInput.value;
webSocket.send(message);
messageInput.value = ''; // Clear the input
}
และนี่ก็เป็นตัวอย่างง่าย ๆ ในการใช้งาน Web Socket นั่นเองงง