Skip to main content
0

สรุปสั้น ๆ

ปกติแล้วเวลาที่เราสร้างเว็บขึ้นมาเว็บนึง เวลา 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 ฟรี !เรียนรู้เพิ่มเติม

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

BorntoDev

Author BorntoDev

BorntoDev Co., Ltd.

More posts by BorntoDev
Close Menu

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

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

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

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

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

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

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

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