Skip to main content
0

Server Sent Event ส่งข้อมูลจาก Server อัปเดตให้ Client ได้แบบง่าย ๆ

สรุปสั้น ๆ

ปกติแล้วเวลาที่เราสร้างเว็บขึ้นมาเว็บนึง เวลา Client ต้องการข้อมูลอะไรก็ทำการเรียกไปที่ Server ผ่าน API เป็นเรื่องปกติที่ทุกคนน่าจะคุ้นเคยกันอยู่แล้ว แต่ถ้าเราอยากจะให้ Server เป็นคนเรียกไปหา Client บ้าง เชื่อว่าจุดนี้หลาย ๆ คนอาจจะต้องเปิดกูเกิลกันบ้างแล้วว่าจะใช้ท่าไหนดี ใช่แล้วครับ วันนี้เราจะมาดูหนึ่งในท่าที่เราสามารถใช้ได้นั่นคือ Server Sent Event นั่นเอง

เขียนโดย
Sutthinai Boonyingyongchai
MidLevel Software Developer

บทความนี้ตีพิมพ์ และ เผยแพร่เมื่อ 07 เมษายน 2566

Server Sent Event คืออะไร ?

Server Sent Event (SSE) เป็นวิธีการที่จะทำให้ Server สามารถเป็นคนส่งข้อมูลไปหา Client ได้ โดยเป็นการสื่อสารจากทางเดียว และหลังจากที่ Client กับ Server เชื่อมต่อกันเรียบร้อยแล้ว Server ก็จะสามารถเริ่มส่งข้อมูลไปหา Client ได้เรื่อย ๆ ตามที่ต้องการ

แนวคิดของ SSE

การสร้าง Connection ระหว่าง Client และ Server นั้นทำผ่าน HTTP Protocol แบบเดียวกันกับเวลาที่ Client เรียก API เพียงแต่ใน Header จะระบุ Content-Type เป็น text/event-stream เพื่อให้รู้ว่าเป็นเส้นสำหรับ SSE
หลังจากนั้น Server ก็จะส่งข้อมูลไปหา Client ได้เรื่อย ๆ ผ่าน Connection เดิมที่เชื่อมต่อกันค้างไว้ จนกว่าฝั่งใดฝั่งหนึ่งจะปิดการชื่อมต่อ ลำดับการติดต่อกันก็จะหน้าตาประมาณนี้

การใช้ SSE

สำหรับงานที่เหมาะจะนำ SSE มาใช้จะเป็นงานที่ต้องการการอัปเดตอย่างต่อเนื่องจากฝั่ง Server ไปยัง Client ตัวอย่างเช่น 

  • จำนวนสินค้าคงเหลือ
  • ราคาสินค้า
  • สถานะพัสดุ
  • แชท
  • Social media feeds

เพื่อให้เข้าใจ SSE มากขึ้นเราลองมาดูตัวอย่างการเขียนจริง ๆ กันดีกว่า โดยที่เราจะสร้าง SSE ที่ส่งข้อมูลราคาหุ้น (แบบสมมติ) ไปให้ Client เรื่อย ๆ

เริ่มที่ฝั่ง Server ที่เราจะใช้ Express.js สำหรับเป็น API Server โดยที่มี API ชื่อว่า ‘/stock-price’ ที่เอาไว้สำหรับเชื่อมต่อ SSE โดยการทำงานข้างในก็จะใช้คำสั่ง setInterval ในการสุ่มเลขทุกวินาทีเพื่อเป็นราคาหุ้นแบบปลอม ๆ ของเรา โค้ดในไฟล์ server.js ก็จะมีหน้าตาแบบด้านล่างนี้

const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());

// API สำหรับเชื่อมต่อรับการอัพเดตราคาหุ้น
app.get('/stock-price', (req, res) => {
  // Set header ให้รู้ว่าเป็น SSE
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  // สมมติว่าตรงนี้เป็นการอัพเดตราคาหุ้นทุก ๆ วินาที
  const intervalId = setInterval(() => {
    const randomNum = Math.floor(Math.random() * 100) + 1;
    res.write(`data: ${randomNum}\n\n`);
  }, 1000);

  // ถ้า Client ปิด Connection ไปแล้วก็ clearInterval ทิ้งด้วย
  req.on('close', () => {
    clearInterval(intervalId);
    res.end();
  });
});

// Start server
const port = 4000;
app.listen(port, () => {
  console.log(`Server started on port ${port}`);
});
const express = require('express');
const cors = require('cors');

const app = express();
app.use(cors());

// API สำหรับเชื่อมต่อรับการอัพเดตราคาหุ้น
app.get('/stock-price', (req, res) => {
  // Set header ให้รู้ว่าเป็น SSE
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  // สมมติว่าตรงนี้เป็นการอัพเดตราคาหุ้นทุก ๆ วินาที
  const intervalId = setInterval(() => {
    const randomNum = Math.floor(Math.random() * 100) + 1;
    res.write(`data: ${randomNum}\n\n`);
  }, 1000);

  // ถ้า Client ปิด Connection ไปแล้วก็ clearInterval ทิ้งด้วย
  req.on('close', () => {
    clearInterval(intervalId);
    res.end();
  });
});

// Start server
const port = 4000;
app.listen(port, () => {
  console.log(`Server started on port ${port}`);
});

ส่วนในฝั่ง Client ก็จะทำการเชื่อมต่อ SSE ผ่านการประกาศ EventSource และใส่ Path ลงไป โดยที่จะมีการ Handle 2 อีเวนท์คือ ‘onopen’ ตอนที่เชื่อมต่อสำเร็จ แล้วก็ ‘onmessage’ ตอนที่ได้รับข้อมูลมาจาก Server กับอีกหนึ่งปุ่มสำหรับปิดการเชื่อมต่อ เราจะได้ไฟล์ index.html หน้าตาดังต่อไปนี้

<!DOCTYPE html>
<html>

<head>
  <title>Stock</title>
</head>

<body>
  <p>status: <span id="connect-status">Connecting</span></p>
  <button id="stop-sse" onclick="stopSSE()">Stop</button>
  <h1>Stock Price:</h1>
  <ul id="stocks"></ul>

  <script>
    const stockElem = document.getElementById('stocks');
    const statusElem = document.getElementById('connect-status');

    // เชื่อมต่อ API ที่ใช้เป็น SSE
    const eventSource = new EventSource('http://localhost:4000/stock-price');

    // Handle ตอนที่เปิดการเชื่อมต่อสำเร็จแล้ว
    eventSource.onopen = (event) => {
      statusElem.textContent = 'Connected'
    }

    // Handle ตอนที่มีข้อความใหม่ส่งมา
    eventSource.onmessage = (event) => {
      const number = JSON.parse(event.data);
      const listItem = document.createElement('li');
      listItem.innerText = number;
      stockElem.appendChild(listItem);
    };

    function stopSSE() {
      eventSource.close();
      statusElem.textContent = 'Closed'
    }
  </script>
</body>

</html>
<!DOCTYPE html>
<html>

<head>
  <title>Stock</title>
</head>

<body>
  <p>status: <span id="connect-status">Connecting</span></p>
  <button id="stop-sse" onclick="stopSSE()">Stop</button>
  <h1>Stock Price:</h1>
  <ul id="stocks"></ul>

  <script>
    const stockElem = document.getElementById('stocks');
    const statusElem = document.getElementById('connect-status');

    // เชื่อมต่อ API ที่ใช้เป็น SSE
    const eventSource = new EventSource('http://localhost:4000/stock-price');

    // Handle ตอนที่เปิดการเชื่อมต่อสำเร็จแล้ว
    eventSource.onopen = (event) => {
      statusElem.textContent = 'Connected'
    }

    // Handle ตอนที่มีข้อความใหม่ส่งมา
    eventSource.onmessage = (event) => {
      const number = JSON.parse(event.data);
      const listItem = document.createElement('li');
      listItem.innerText = number;
      stockElem.appendChild(listItem);
    };

    function stopSSE() {
      eventSource.close();
      statusElem.textContent = 'Closed'
    }
  </script>
</body>

</html>

ผลลัพธ์ที่ได้จะเห็นว่าตอนแรก status จะเป็น Connecting แล้วเปลี่ยนเป็น Connected ตอนที่เชื่อมต่อสำเร็จและมีการแสดงตัวเลขที่ได้รับจาก Server โดยการใส่ <li> ใหม่เพิ่มลงไปเรื่อย ๆ สุดท้ายพอกดปุ่มเพื่อปิดการเชื่อมต่อก็จะไม่มีการรับข้อมูลมาใส่เพิ่มแล้วนั่นเอง

ข้อจำกัด

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

  • Unidirectional – ถูกออกแบบมาให้ใช้เพื่อสื่อสารเพียงทางเดียว ถ้าหากต้องการสื่อสารสองทางอาจจะต้องเลือกใช้ WebSocket แทน
  • Text Base – SSE นั้นรองรับการส่งข้อมูลในรูปแบบของข้อความเท่านั้น ไม่สามารถส่ง Binary ได้

Concurrent Connections – การใช้ SSE นั้นถูกจำกัดจำนวนการเชื่อมต่อสูงสุดเอาไว้โดยเบราเซอร์ โดยนับเป็นรายโดเมน ทำให้การเปิดเว็บหลาย ๆ แท็บ อาจจะเกินจำนวนสูงสุดได้ (javascript – Server sent events and browser limits – Stack Overflow)

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

ระบบฝึกทักษะ การเขียนโปรแกรม

ที่พร้อมตรวจผลงานคุณ 24 ชั่วโมง

  • โจทย์ปัญหากว่า 200 ข้อ ที่รอท้าทายคุณอยู่
  • รองรับ 9 ภาษาโปรแกรมหลัก ไม่ว่าจะ Java, Python, C ก็เขียนได้
  • ใช้งานได้ฟรี ! ครบ 20 ข้อขึ้นไป รับ Certificate ไปเลย !!
เข้าใช้งานระบบ DevLab ฟรี !เรียนรู้เพิ่มเติม

เรียนรู้ไอที “อัพสกิลเขียนโปรแกรม” จากตัวจริง
ปั้นให้คุณเป็น คนสายไอทีระดับมืออาชีพ

4

แนะนำสำหรับคุณ

Close Menu

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

ตั้งค่าความเป็นส่วนตัว

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

ยอมรับทั้งหมด
จัดการความเป็นส่วนตัว
  • คุกกี้ที่จำเป็น
    เปิดใช้งานตลอด

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

  • คุกกี้สำหรับการติดตามทางการตลาด

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

บันทึกการตั้งค่า