阿里云主机折上折
  • 微信号
Current Site:Index > The usage of Server-Sent Events (SSE)

The usage of Server-Sent Events (SSE)

Author:Chuan Chen 阅读数:7484人阅读 分类: HTML

What are Server-Sent Events

Server-Sent Events (SSE) is an HTML5 technology that allows servers to push real-time data to clients. Unlike WebSocket, SSE is a one-way communication method, supporting only server-to-client message pushing. It is based on the HTTP protocol, uses a simple event stream format, and is suitable for scenarios where the server needs to actively push data but the client does not need to send data frequently.

Key features of SSE include:

  • Simple to use, requiring only the JavaScript API
  • Automatic reconnection mechanism
  • Support for custom event types
  • Lightweight, with no additional protocols needed

Comparison Between SSE and WebSocket

SSE and WebSocket are both technologies for real-time communication, but each has its own use cases:

Feature SSE WebSocket
Communication Direction One-way (server → client) Two-way
Protocol HTTP Independent WebSocket protocol
Connection Complexity Simple More complex
Data Format Text (event stream) Binary or text
Automatic Reconnection Supported Requires manual implementation
Browser Support Newer browsers Widely supported

SSE is more suitable for the following scenarios:

  • Applications that only require server-pushed data
  • Real-time features that need simple implementation
  • Systems that do not require frequent two-way communication

Basic Usage

Client-Side Implementation

The client uses the EventSource API to receive messages pushed by the server:

// Create an EventSource object connected to the server endpoint
const eventSource = new EventSource('/sse-endpoint');

// Listen for the default message event
eventSource.onmessage = function(event) {
  console.log('Message received:', event.data);
  // Update page content
  document.getElementById('messages').innerHTML += event.data + '<br>';
};

// Listen for custom events
eventSource.addEventListener('customEvent', function(event) {
  console.log('Custom event:', event.data);
});

// Error handling
eventSource.onerror = function(error) {
  console.error('EventSource error:', error);
  // Custom reconnection logic can be implemented here
};

Server-Side Implementation (Node.js Example)

const http = require('http');
const fs = require('fs');

http.createServer((req, res) => {
  if (req.url === '/sse-endpoint') {
    // Set the required response headers for SSE
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive'
    });
    
    // Send an initial message
    res.write('data: Connection established\n\n');
    
    // Send messages periodically
    let counter = 0;
    const intervalId = setInterval(() => {
      counter++;
      res.write(`data: This is message ${counter}\n\n`);
      
      // Send a custom event
      if (counter % 3 === 0) {
        res.write(`event: customEvent\ndata: Custom event data ${counter}\n\n`);
      }
      
      // End after 10 tests
      if (counter >= 10) {
        clearInterval(intervalId);
        res.end();
      }
    }, 1000);
    
    // Clean up when the client disconnects
    req.on('close', () => {
      clearInterval(intervalId);
    });
  } else {
    // Return the HTML page
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.end(fs.readFileSync('./sse-client.html'));
  }
}).listen(3000);

console.log('SSE server running at http://localhost:3000');

Event Stream Format

SSE uses a specific text stream format, with each message consisting of the following parts:

event: eventName  // Optional, specifies the event type
data: messageData // Message content, can be multi-line
id: messageId    // Optional, message ID
retry: timeout   // Optional, reconnection time (milliseconds)
\n               // Empty line indicates the end of the message

Example:

event: statusUpdate
data: {"user": "John", "status": "online"}
id: 12345
retry: 5000

data: This is a multi-line
data: message example

Advanced Features

Custom Event Types

In addition to the default message event, SSE supports custom event types:

// Server-side sending of a custom event
res.write('event: userUpdate\ndata: {"id": 101, "name": "Jane"}\n\n');

// Client-side listening for custom events
eventSource.addEventListener('userUpdate', function(event) {
  const userData = JSON.parse(event.data);
  console.log('User update:', userData.name);
});

Message ID and Reconnection Mechanism

SSE automatically tracks the last received message ID and sends it to the server via the Last-Event-ID header when reconnecting after a disconnection:

// Server-side sending of a message with an ID
res.write('id: 1001\ndata: Important message\n\n');

// Server can check the Last-Event-ID header
const lastEventId = req.headers['last-event-id'];
if (lastEventId) {
  // Resume sending messages from the breakpoint
}

Controlling Reconnection Time

You can specify the retry time after a client disconnection:

// Server suggests the client reconnect after 3 seconds
res.write('retry: 3000\n\n');

Practical Application Examples

Real-Time Stock Price Updates

// Client code
const stockSource = new EventSource('/stocks');

stockSource.addEventListener('priceUpdate', function(event) {
  const stockData = JSON.parse(event.data);
  const stockElement = document.getElementById(`stock-${stockData.symbol}`);
  if (stockElement) {
    stockElement.textContent = `${stockData.symbol}: $${stockData.price.toFixed(2)}`;
    stockElement.style.color = stockData.change >= 0 ? 'green' : 'red';
  }
});

// Server-side code (Node.js)
setInterval(() => {
  const stocks = ['AAPL', 'GOOGL', 'MSFT', 'AMZN'];
  stocks.forEach(symbol => {
    const priceChange = (Math.random() - 0.5) * 10;
    const stockData = {
      symbol,
      price: 100 + Math.random() * 1000,
      change: priceChange
    };
    res.write(`event: priceUpdate\ndata: ${JSON.stringify(stockData)}\n\n`);
  });
}, 2000);

Real-Time Log Monitoring

// Client code
const logSource = new EventSource('/logs');

logSource.onmessage = function(event) {
  const logElement = document.getElementById('log-output');
  logElement.value += event.data + '\n';
  logElement.scrollTop = logElement.scrollHeight;
};

// Server-side code (simulating logs)
const fs = require('fs');
const tail = require('tail').Tail;

const logFile = new Tail('/var/log/app.log');

logFile.on('line', (line) => {
  res.write(`data: ${line}\n\n`);
});

Browser Compatibility and Fallback Solutions

SSE is well-supported in modern browsers but may not be available in IE and some mobile browsers. You can detect compatibility and provide fallback solutions as follows:

if (typeof EventSource !== 'undefined') {
  // Use SSE
  const source = new EventSource('/updates');
} else {
  // Fallback: polling or other technologies
  console.warn('Browser does not support Server-Sent Events, using polling instead');
  setInterval(fetchUpdates, 5000);
  
  function fetchUpdates() {
    fetch('/updates')
      .then(response => response.json())
      .then(data => {
        // Handle updates
      });
  }
}

Security Considerations

When using SSE, the following security issues should be noted:

  1. Cross-Origin Requests: SSE follows the same-origin policy by default. Cross-origin requests require CORS headers:

    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Access-Control-Allow-Origin': 'https://client-domain.com'
    });
    
  2. Authentication and Authorization: Credentials can be passed in the EventSource constructor:

    const eventSource = new EventSource('/secure-sse', {
      withCredentials: true
    });
    
  3. Message Validation: Always validate data sent by the server to prevent XSS attacks:

    eventSource.onmessage = function(event) {
      const safeData = escapeHtml(event.data);
      // Update the DOM with safeData
    };
    

Performance Optimization

  1. Connection Management: Close unnecessary EventSource connections promptly:

    // When no longer needing to receive messages
    function stopUpdates() {
      eventSource.close();
    }
    
  2. Server-Side Resource Release: Ensure server resources are released when the client disconnects:

    req.on('close', () => {
      // Clean up timers, database connections, etc.
      clearInterval(updateInterval);
    });
    
  3. Message Compression: For large amounts of text data, consider server-side compression:

    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Content-Encoding': 'gzip'
    });
    

Integration with Other Technologies

Working with Service Workers

You can handle SSE messages in a Service Worker to implement offline functionality:

// service-worker.js
self.addEventListener('message', event => {
  if (event.data.type === 'CREATE_SSE') {
    const sse = new EventSource(event.data.url);
    sse.onmessage = msg => {
      // Process the message and decide whether to show a notification
      if (msg.data.important) {
        self.registration.showNotification('New notification', {
          body: msg.data.text
        });
      }
    };
  }
});

// Main thread
navigator.serviceWorker.controller.postMessage({
  type: 'CREATE_SSE',
  url: '/notifications'
});

Integration with React/Vue and Other Frameworks

Using SSE in a React component:

import { useEffect, useState } from 'react';

function StockTicker() {
  const [stocks, setStocks] = useState({});
  
  useEffect(() => {
    const source = new EventSource('/stocks');
    
    source.addEventListener('priceUpdate', event => {
      const stockData = JSON.parse(event.data);
      setStocks(prev => ({
        ...prev,
        [stockData.symbol]: stockData
      }));
    });
    
    return () => source.close(); // Clean up the effect
  }, []);
  
  return (
    <div>
      {Object.values(stocks).map(stock => (
        <div key={stock.symbol}>
          {stock.symbol}: ${stock.price.toFixed(2)}
        </div>
      ))}
    </div>
  );
}

Debugging and Troubleshooting

  1. Viewing the Event Stream: You can directly access the SSE endpoint in the browser address bar to view the raw event stream.

  2. Network Inspection: Use the developer tools' Network panel to check the SSE connection status and transmitted messages.

  3. Common Issues:

    • Ensure the server response includes the correct Content-Type: text/event-stream header
    • Each message must end with two newline characters (\n\n)
    • Avoid single newline characters in message data. For multi-line data, each line should start with data:
  4. Enhanced Error Handling:

    eventSource.onerror = function(error) {
      if (eventSource.readyState === EventSource.CLOSED) {
        console.log('Connection closed by the server');
      } else {
        console.error('SSE error:', error);
        // Attempt to reconnect
        setTimeout(() => {
          initEventSource();
        }, 5000);
      }
    };
    

Extended Application Scenarios

  1. Real-Time Notification Systems: Message notifications in social networks and collaboration tools.

  2. Real-Time Dashboards: Real-time data display for monitoring systems and analytics platforms.

  3. Real-Time Collaborative Editing: Cursor position synchronization in collaborative document editing.

  4. Sports Event Live Updates: Real-time score and match statistics updates.

  5. Auction Systems: Real-time bid updates and countdowns.

  6. IoT Device Monitoring: Real-time pushing of device status and sensor data.

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.