SSE (Server-Sent Events) เป็นมาตรฐาน W3C ที่ช่วยให้เซิร์ฟเวอร์สามารถส่งข้อมูลสตรีมไปยังไคลเอนต์ผ่านการเชื่อมต่อ HTTP เดียวในโหมดทางเดียวได้ แตกต่างจาก WebSocket, SSE ทำงานบน HTTP ทั่วไปและไม่ต้องการโปรโตคอลพิเศษหรือไลบรารีทางฝั่งไคลเอนต์ ตาม ข้อกำหนด W3C HTML Living Standard (2025), EventSource API รองรับในทุก เบราว์เซอร์ที่ทันสมัย รวมถึง Chrome, Firefox, Safari และ Edge
โดยสรุป
SSE (Server-Sent Events) เป็นเทคโนโลยีที่ช่วยให้เว็บเซิร์ฟเวอร์สามารถส่งข้อมูลไปยังไคลเอนต์ได้ตลอดเวลาหลังจากสร้างการเชื่อมต่อ มันได้รับการมาตรฐานโดย WHATWG เป็นส่วนหนึ่งของ HTML Living Standard และใช้ MIME ชนิด text/event-stream SSE รองรับการส่งข้อมูลข้อความโดยสามารถระบุตัวระบุข้อความ, ชนิดของเหตุการณ์ และเวลาหน่วงในการเชื่อมต่อใหม่
แตกต่างจาก WebSocket ที่ต้องการ โปรโตคอลสองทาง และคำขออัปเกรด, SSE ทำงานบน HTTP ทั่วไป เซิร์ฟเวอร์จะตั้งค่าส่วนหัว Content-Type: text/event-stream, ส่งข้อมูลเป็นส่วนๆ และเก็บการเชื่อมต่อไว้ ไคลเอนต์จะรับข้อมูลผ่าน EventSource API ของเบราว์เซอร์, ซึ่งจะวิเคราะห์สตรีมโดยอัตโนมัติและสร้างเหตุการณ์
ตาม CanIUse (2025), EventSource API รองรับใน 97.5% ของเบราว์เซอร์ทั่วโลก ไม่รองรับใน Internet Explorer และเบราว์เซอร์มือถือบางตัว (Samsung Internet ก่อนรุ่น 7.0) สำหรับกรณีเหล่านี้, มีโพลีฟิลที่จำลอง EventSource ผ่าน XHR streaming SSE ไม่ทำงานกับ HTTP/1.1 pipelining แต่เข้ากันได้อย่างสมบูรณ์กับ HTTP/2 server push
SSE ถูกเสนอเป็นส่วนหนึ่งของข้อกำหนด HTML5 ในปี 2009 ภายใต้ชื่อ Server-Sent DOM Events การนำไปใช้ครั้งแรกปรากฏใน Opera 9.0, จากนั้น Firefox 6.0 (2011), Chrome 9.0 (2011) และ Safari 5.0 (2010) ในปี 2015, ข้อกำหนดได้ถูกย้ายไปยังส่วนแยกของ HTML Living Standard แม้จะมีประวัติศาสตร์กว่าสิบปี, SSE ก็ยังไม่เป็นที่นิยมเท่า WebSocket เนื่องจากลักษณะทางเดียวของมัน
กลไกการทำงานของ SSE มีดังนี้: ไคลเอนต์สร้างอินสแตนซ์ EventSource ด้วย URL ของจุดสิ้นสุดของเซิร์ฟเวอร์ เบราว์เซอร์จะส่งคำขอ GET ด้วยส่วนหัว Accept: text/event-stream เซิร์ฟเวอร์จะตอบกลับด้วยสถานะ 200 OK และส่วนหัว Content-Type: text/event-stream, จากนั้นเริ่มส่งข้อมูลในรูปแบบ event-stream การเชื่อมต่อจะเปิดอยู่จนกว่าเซิร์ฟเวอร์จะส่งสัญญาณสิ้นสุดหรือไคลเอนต์เรียก close()
ฝั่งเซิร์ฟเวอร์, ข้อมูลจะถูกส่งเป็นส่วนๆ (chunked transfer encoding) แต่ละส่วนข้อมูลคือข้อความที่ประกอบด้วยบรรทัดของฟิลด์ (event, data, id, retry) เซิร์ฟเวอร์สามารถส่งข้อความได้ตลอดเวลา, ทำให้ SSE เหมาะสำหรับการแจ้งเตือนและการอัปเดตสถานะ การเชื่อมต่อไม่ต้องการการแลกเปลี่ยนแพ็กเก็ต heartbeat อย่างต่อเนื่อง (เช่น WebSocket), แม้ว่าฟิลด์ retry จะควบคุมความถี่ในการเชื่อมต่อใหม่
ตาม การทดสอบประสิทธิภาพ (2024), SSE ให้ปริมาณงานสูงถึง 10,000 ข้อความต่อวินาทีต่อการเชื่อมต่อ โดยมีขนาดข้อความ 256 ไบต์ ฝั่งเซิร์ฟเวอร์, แต่ละการเชื่อมต่อ SSE ใช้หน่วยความจำประมาณ 5–10 KB, ทำให้เซิร์ฟเวอร์หนึ่งตัวสามารถรองรับการเชื่อมต่อพร้อมกันมากกว่า 50,000 รายการด้วย 1 GB RAM ซึ่งน้อยกว่า WebSocket มากเนื่องจากไม่มีโปรโตคอลไบนารี
รูปแบบ text/event-stream เป็นโปรโตคอลข้อความที่ง่าย โดยแต่ละข้อความประกอบด้วยฟิลด์ที่มีชื่อซึ่งคั่นด้วยอักขระขึ้นบรรทัดใหม่ แต่ละฟิลด์มีรูปแบบ “ชื่อฟิลด์: ค่า” ข้อความจะถูกคั่นด้วยอักขระขึ้นบรรทัดใหม่สองตัว (\n\n)
ฟิลด์ที่รองรับ: event (ชนิดของเหตุการณ์, ค่าเริ่มต้นคือ message), data (สตริงข้อมูล, สามารถมีหลายบรรทัดได้), id (ตัวระบุเหตุการณ์ล่าสุด, เก็บไว้ใน Last-Event-ID), retry (เวลาหน่วงในการเชื่อมต่อใหม่ในหน่วยมิลลิวินาที) ความคิดเห็นเริ่มต้นด้วยเครื่องหมายจุลภาค (:) และถูกละเว้นโดยตัวแยกวิเคราะห์ แต่สามารถใช้สำหรับ heartbeat ได้
| ฟิลด์ | จำเป็น | วัตถุประสงค์ |
|---|---|---|
| event | ไม่ | ชนิดของเหตุการณ์ (ค่าเริ่มต้นคือ message) |
| data | ใช่ | สตริงข้อมูลของข้อความ |
| id | ไม่ | ตัวระบุเหตุการณ์สำหรับ Last-Event-ID |
| retry | ไม่ | เวลาหน่วงในการเชื่อมต่อใหม่ในหน่วย ms |
: heartbeat comment
event: update
data: {"user": "Alice", "action": "typing"}
id: 1001
event: notification
data: {"type": "info", "text": "New version available"}
data: {"type": "action", "url": "/upgrade"}
retry: 3000
event: close
data: Session ended
EventSource API เป็นอินเทอร์เฟสของเบราว์เซอร์ในตัวสำหรับรับ SSE เพื่อสร้างการเชื่อมต่อ, เพียงเรียกคอนสตรักเตอร์ด้วย URL ของจุดสิ้นสุด EventSource จะสร้างการเชื่อมต่อโดยอัตโนมัติ, จัดการการเชื่อมต่อใหม่ และแยกวิเคราะห์ข้อความขาเข้าเป็นเหตุการณ์ JavaScript
เหตุการณ์ของ EventSource: open (การเชื่อมต่อถูกสร้าง), message (ได้รับข้อความโดยไม่ระบุเหตุการณ์), error (ข้อผิดพลาดของการเชื่อมต่อ) สำหรับเหตุการณ์ที่กำหนดเอง (event: custom), คุณสามารถใช้ addEventListener กับชื่อของเหตุการณ์ได้ EventSource จะส่งส่วนหัว Last-Event-ID โดยอัตโนมัติเมื่อเชื่อมต่อใหม่, ทำให้เซิร์ฟเวอร์สามารถเริ่มสตรีมใหม่จากจุดที่ถูกขัดจังหวะได้
ตาม เอกสาร MDN (2025), EventSource รองรับ CORS และการส่งข้อมูลรับรองความถูกต้อง (withCredentials) EventSource ไม่เหมาะสำหรับการส่งส่วนหัวที่กำหนดเองหรือเนื้อความของคำขอ — จำเป็นต้องใช้การนำไปใช้แบบเองโดยใช้ fetch + ReadableStream EventSource ไม่รองรับข้อมูลไบนารี — เฉพาะข้อความและ JSON
const eventSource = new EventSource('/api/events/stream');
eventSource.addEventListener('open', () => {
console.log('เปิดการเชื่อมต่อ SSE แล้ว');
});
eventSource.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log('ได้รับ:', data);
renderUpdate(data);
});
eventSource.addEventListener('notification', (event) => {
const notification = JSON.parse(event.data);
showNotification(notification.text);
});
eventSource.addEventListener('error', (error) => {
console.error('ข้อผิดพลาด SSE:', error);
// เบราว์เซอร์เชื่อมต่อใหม่อัตโนมัติ
});
// ปิดการเชื่อมต่อ
eventSource.close();
SSE และ WebSocket เป็นเทคโนโลยีที่แตกต่างกันสำหรับการสื่อสารแบบเรียลไทม์, แต่ละแบบมีจุดแข็งของตัวเอง WebSocket เหมาะสำหรับการแลกเปลี่ยนข้อมูลสองทาง (แชท, เกม, การแก้ไขร่วมกัน), ในขณะที่ SSE สำหรับสตรีมทางเดียวจากเซิร์ฟเวอร์ไปยังไคลเอนต์ (การแจ้งเตือน, ฟีดข่าว, ตัวเลข)
ความแตกต่างหลัก คือ WebSocket ต้องการคำขออัปเกรดจาก HTTP/1.1 ไปยังโปรโตคอล WebSocket (ws://), ซึ่งอาจถูกบล็อกโดยพร็อกซีขององค์กร SSE ทำงานบน HTTP ทั่วไป, ผ่านพร็อกซีใดๆ ก็ได้ และไม่ต้องการการกำหนดค่าเซิร์ฟเวอร์พิเศษ SSE ยังนำไปใช้ได้ง่ายกว่า — เซิร์ฟเวอร์ไม่ต้องการไลบรารีเพิ่มเติม, แค่จัดรูปแบบการตอบกลับ HTTP ให้ถูกต้อง
ตาม การทดสอบเปรียบเทียบ (2024), บนกระบวนการเซิร์ฟเวอร์เดียว SSE รองรับการเชื่อมต่อมากกว่า WebSocket 30–50% เนื่องจากโปรโตคอลที่ง่ายกว่า อย่างไรก็ตาม, SSE มีเวลาแฝงสูงกว่า (50–200 ms เทียบกับ 10–50 ms ของ WebSocket) เนื่องจาก SSE ใช้ HTTP แบบเป็นส่วนๆ แทนที่จะเป็นสตรีมสองทางที่สมบูรณ์ด้วยเฟรมไบนารี
| ลักษณะ | SSE | WebSocket |
|---|---|---|
| ทิศทาง | เซิร์ฟเวอร์ → ไคลเอนต์ | สองทาง |
| โปรโตคอล | HTTP (text/event-stream) | ws:// / wss:// (RFC 6455) |
| เบราว์เซอร์ | 97.5% (EventSource ในตัว) | 97% (WebSocket ในตัว) |
| ข้อมูล | เฉพาะข้อความ / JSON | ข้อความ + ไบนารี (Blob, ArrayBuffer) |
| การจัดการพร็อกซี | ผ่านพร็อกซีใดก็ได้ | ต้องการการตั้งค่าพร็อกซี |
| การเชื่อมต่อใหม่ | อัตโนมัติ (เบราว์เซอร์) | การนำไปใช้ด้วยตนเอง |
| ประวัติ | Last-Event-ID | ไม่มีประวัติในตัว |
การนำ SSE ไปใช้บนเซิร์ฟเวอร์ ไม่ต้องการไลบรารี — แค่ตั้งค่าส่วนหัว HTTP ที่ถูกต้องและส่งข้อมูลในรูปแบบ text/event-stream มาดูตัวอย่างใน Node.js โดยใช้โมดูล http ในตัว เซิร์ฟเวอร์จะตั้งค่าส่วนหัว Content-Type และ Cache-Control, จากนั้นส่งข้อความทุก N วินาที
ตาม MDN Web Docs (2025), ส่วนหัวที่จำเป็นสำหรับ SSE คือ: Content-Type: text/event-stream, Cache-Control: no-cache และ Connection: keep-alive หากไม่มี Cache-Control, เบราว์เซอร์อาจแคชสตรีม SSE, ซึ่งจะหยุดการจัดส่ง Connection: keep-alive จะบอกเบราว์เซอร์อย่างชัดแจ้งให้เก็บการเชื่อมต่อไว้
const http = require('http');
http.createServer((req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
let eventId = 0;
const interval = setInterval(() => {
eventId++;
res.write(`id: ${eventId}\n`);
res.write(`event: update\n`);
res.write(`data: {"time": "${new Date().toISOString()}", "id":${eventId}}\n\n`);
}, 2000);
req.on('close', () => {
clearInterval(interval);
});
}).listen(3000);
from flask import Response, Flask
import time
import json
app = Flask(__name__)
@app.route('/stream')
def stream():
def generate():
event_id = 0
while True:
event_id += 1
data = json.dumps(
{'ticker': 'AAPL', 'price': 150.25})
yield f'id: {event_id}\nevent: price\ndata: {data}\n\n'
time.sleep(1)
return Response(generate(),
mimetype='text/event-stream')
การใช้ SSE ในแอพมือถือ ถูกจำกัดโดยการขาดการนำไปใช้ EventSource ดั้งเดิมสำหรับ iOS และ Android บนแพลตฟอร์มมือถือ, SSE ถูกนำไปใช้ผ่านไลบรารีของบุคคลที่สาม: บน iOS — ผ่าน URLSession กับ NSURLProtocol, บน Android — ผ่าน OkHttp กับการรองรับ SSE (okhttp-sse) สำหรับ React Native และ Flutter, มีแพ็กเกจที่จำลอง EventSource
บน iOS, การนำไปใช้ SSE ดั้งเดิมเป็นไปได้ผ่าน URLSessionDataDelegate เมื่อรับข้อมูลในเมธอด urlSession(_:dataTask:didReceive:), แอพจะสะสมบัฟเฟอร์และแยกวิเคราะห์รูปแบบ event-stream ด้วยตนเอง ตาม บล็อกการพัฒนา iOS (2024), การใช้แบตเตอรี่กับ SSE บน iOS ต่ำกว่าการเชื่อมต่อ WebSocket คงที่ 40% เนื่องจากไม่มีแพ็กเก็ต heartbeat
บน Android, OkHttp ให้คลาส EventSource.Factory สำหรับสมัครรับสตรีม SSE แอพ Android สามารถใช้ SSE สำหรับการแจ้งเตือนเมื่อ FCM ไม่พร้อมใช้งาน หรือสำหรับการซิงค์ข้อมูลในพื้นหลัง SSE บน Android ทำงานได้ดีกับ WorkManager สำหรับงานพื้นหลังที่มีอายุยืนยาว ตาม เอกสาร OkHttp (2025), okhttp-sse รองรับการเชื่อมต่อใหม่อัตโนมัติด้วย listener ที่กำหนดเอง
คำถามที่พบบ่อย
SSE คือการส่งทางเดียว (เซิร์ฟเวอร์ → ไคลเอนต์) บน HTTP, ไม่ต้องการไลบรารีบนไคลเอนต์ WebSocket คือการส่งสองทางด้วยโปรโตคอลไบนารี SSE นำไปใช้ได้ง่ายกว่า, WebSocket เหมาะสำหรับงานที่ไคลเอนต์ก็ส่งข้อมูลเช่นกัน
ไม่, SSE ส่งเฉพาะ ข้อมูลข้อความ สำหรับข้อมูลไบนารี (รูปภาพ, เสียง), จำเป็นต้องใช้การเข้ารหัส Base64, ซึ่งเพิ่มขนาดขึ้น 33% สำหรับสตรีมไบนารี, ควรใช้ WebSocket
EventSource จะเชื่อมต่อใหม่โดยอัตโนมัติเมื่อเกิดการขาดการเชื่อมต่อ เวลาหน่วงถูกกำหนดโดยฟิลด์ retry ในสตรีม (ค่าเริ่มต้น 1000 ms) เมื่อเชื่อมต่อใหม่, เบราว์เซอร์จะส่งส่วนหัว Last-Event-ID, ทำให้เซิร์ฟเวอร์สามารถเริ่มสตรีมใหม่จากจุดที่ถูกขัดจังหวะได้
แต่ละเบราว์เซอร์มีขีดจำกัดของจำนวนการเชื่อมต่อ HTTP พร้อมกันกับโดเมนเดียว สำหรับ HTTP/1.1 — 6–8 การเชื่อมต่อ ต่อโดเมน, สำหรับ HTTP/2 — ถึง 100 ครั้ง SSE ใช้การเชื่อมต่อเดียว, ดังนั้นจึงไม่มีการแข่งขันกับคำขออื่นๆ
SSE เหมาะ เฉพาะสำหรับการรับข้อความ (ขาเข้า) สำหรับการส่งข้อความ (ขาออก), จำเป็นต้องมีคำขอ HTTP แยก (POST) สำหรับแชทที่สมบูรณ์, สะดวกกว่าในการใช้ WebSocket หรือ Socket.IO กับการสื่อสารสองทางในการเชื่อมต่อเดียว
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม