مقدمه

در درس قبل با Web Sockets و قدرت آن برای ارتباطات دوطرفه آشنا شدیم. اما در بسیاری از سناریوها، ما فقط به یک کانال ارتباطی یک‌طرفه از سرور به کلاینت نیاز داریم. برای مثال، یک وب‌سایت خبری که می‌خواهد آخرین اخبار را به کاربران نمایش دهد، یا یک داشبورد که قیمت لحظه‌ای سهام را نشان می‌دهد. در این موارد، کلاینت عمدتاً یک شنونده است و نیازی به ارسال مداوم داده به سرور ندارد.

برای این نوع کاربردها، یک تکنولوژی ساده‌تر و سبک‌تر به نام Server-Sent Events (SSE) یا «رویدادهای ارسال‌شده از سرور» وجود دارد. SSE به یک صفحه وب اجازه می‌دهد تا پس از برقراری یک ارتباط اولیه، به صورت خودکار به‌روزرسانی‌ها را از سرور دریافت کند. این ارتباط روی پروتکل استاندارد HTTP برقرار می‌شود و در سمت کلاینت، با استفاده از رابط EventSource پیاده‌سازی می‌شود.

EventSource در مقابل WebSocket

انتخاب بین SSE و WebSocket بستگی به نیاز پروژه شما دارد. درک تفاوت‌های کلیدی آنها به این انتخاب کمک می‌کند:

  • جهت ارتباط: SSE کاملاً یک‌طرفه (از سرور به کلاینت) است. WebSocket دوطرفه است و هر دو طرف می‌توانند هر زمان که بخواهند پیام ارسال کنند.
  • پروتکل: SSE روی HTTP/HTTPS استاندارد کار می‌کند و به زیرساخت خاصی نیاز ندارد. WebSocket از پروتکل ws:// یا wss:// استفاده می‌کند که نیازمند پشتیبانی در سطح سرور است.
  • نوع داده: SSE فقط برای ارسال متن با انکودینگ UTF-8 طراحی شده است. WebSocket هم از متن و هم از داده‌های باینری خام پشتیبانی می‌کند.
  • اتصال مجدد خودکار: EventSource به صورت داخلی قابلیت اتصال مجدد خودکار در صورت قطع شدن ارتباط را دارد. در WebSocket، این منطق باید به صورت دستی پیاده‌سازی شود.
  • پیچیدگی: SSE به دلیل سادگی و استفاده از HTTP، معمولاً پیاده‌سازی آسان‌تری هم در سمت کلاینت و هم در سمت سرور دارد.

نتیجه‌گیری: اگر فقط نیاز به ارسال داده از سرور به کلاینت دارید (مانند سیستم نوتیفیکیشن یا فید خبری)، SSE انتخاب بهینه‌تر و ساده‌تری است. اگر به یک کانال ارتباطی کاملاً دوطرفه (مانند یک اپلیکیشن چت) نیاز دارید، WebSocket تنها گزینه است.

پیاده‌سازی در سمت کلاینت با EventSource

کار با SSE در سمت کلاینت بسیار ساده است. کافیست یک نمونه جدید از EventSource با آدرس URL اندپوینتی که داده‌ها را ارسال می‌کند، بسازید. سپس با استفاده از شنوندگان رویداد، پیام‌ها را دریافت می‌کنید.

رویدادهای اصلی

  • open: زمانی که اتصال با موفقیت برقرار می‌شود، اجرا می‌شود.
  • message: این شنونده پیش‌فرض است و برای هر پیامی که از سرور می‌آید و نام رویداد مشخصی ندارد، اجرا می‌شود. داده‌ی پیام در پراپرتی event.data قرار دارد.
  • error: در صورت بروز خطا در اتصال، اجرا می‌شود. پس از این رویداد، مرورگر به صورت خودکار تلاش می‌کند تا دوباره متصل شود.
  • رویدادهای نام‌گذاری شده: سرور می‌تواند پیام‌هایی با نام رویداد سفارشی ارسال کند (مثلاً event: user-update). در این صورت، ما می‌توانیم با eventSource.addEventListener('user-update', ...) به طور خاص به آن رویداد گوش دهیم.
Copy Icon JAVASCRIPT
// The URL must point to a server endpoint that sends data with 'text/event-stream' content type.
const eventSource = new EventSource('/sse-endpoint');

// Fires when the connection is established
eventSource.onopen = (event) => {
    console.log('Connection to server established.');
};

// Fires for messages without a specific event name
eventSource.onmessage = (event) => {
    console.log('Generic message received:', event.data);
};

// Listen for a custom 'update' event from the server
eventSource.addEventListener('user-update', (event) => {
    const userData = JSON.parse(event.data);
    console.log('Received a user update:', userData);
});

// Fires when an error occurs
eventSource.onerror = (event) => {
    console.error('EventSource failed:', event);
    // Browser will try to reconnect automatically. We can close it to prevent that.
    // eventSource.close();
};

این کد یک اتصال EventSource برقرار کرده و به رویدادهای مختلف آن گوش می‌دهد. مشاهده می‌کنید که یک شنونده برای پیام‌های عمومی (onmessage) و یک شنونده اختصاصی برای پیام‌های با نام user-update ثبت شده است.

فرمت داده در سمت سرور (نگاه کلی)

برای تکمیل درک شما، خوب است بدانید که سرور چگونه داده‌ها را ارسال می‌کند. سرور باید هدر Content-Type را برابر با text/event-stream تنظیم کند و سپس پیام‌ها را در یک فرمت متنی ساده ارسال کند. هر پیام با data: شروع شده و با دو کاراکتر خط جدید (\n\n) از پیام بعدی جدا می‌شود.

Copy Icon SERVER-SIDE TEXT STREAM
data: This is the first message.

event: user-update
data: {"id": 123, "status": "online"}

: this is a comment
data: This is another message.

همانطور که می‌بینید، سرور با استفاده از پیشوند event: می‌تواند نام یک رویداد سفارشی را مشخص کند که در کلاینت با addEventListener قابل دریافت است.

در این درس با EventSource API به عنوان یک روش ساده و کارآمد برای دریافت به‌روزرسانی‌های زنده از سرور آشنا شدیم. این تکنولوژی برای اطلاع‌رسانی‌های یک‌طرفه، جایگزین بسیار خوبی برای Web Sockets است. با این درس، فصل «درخواست‌های شبکه و منابع Remote» به پایان می‌رسد. ما در این فصل از Fetch API و CORS گرفته تا Web Sockets و Server-Sent Events، با طیف وسیعی از ابزارهای مدرن برای ارتباط با سرورها آشنا شدیم. در فصل بعدی، به سراغ یکی از مهم‌ترین جنبه‌های ساخت وب اپلیکیشن‌های پیشرفته، یعنی «ذخیره‌سازی داده‌ها در سمت کاربر» خواهیم رفت.