सर्वर के भेजे गए इवेंट के साथ अपडेट स्ट्रीम करें

सर्वर से भेजे गए इवेंट (एसएसई), क्लाइंट को सर्वर से एचटीटीपी वाले अपने-आप अपडेट भेजते हैं कनेक्शन. कनेक्शन स्थापित होने के बाद, सर्वर डेटा शुरू कर सकते हैं ट्रांसमिशन.

अपने वेब ऐप्लिकेशन से पुश नोटिफ़िकेशन भेजने के लिए, हो सकता है कि आप एसएसई का इस्तेमाल करना चाहें. एसएसई एक दिशा में जानकारी भेजते हैं. इसलिए, आपको क्लाइंट.

एसएसई का सिद्धांत जाना-पहचाना हो सकता है. वेब ऐप्लिकेशन "सदस्यता लेता है" की एक स्ट्रीम में अपडेट सर्वर से जनरेट होते हैं और जब भी कोई नया इवेंट होता है, तो एक सूचना क्लाइंट को भेजी जाती है. हालांकि, सर्वर से भेजे गए इवेंट को समझने के लिए, हमें अपने AJAX से पहले के बाज़ारों की सीमाओं को समझें. इसमें इस तरह का कॉन्टेंट शामिल है:

  • पोलिंग: ऐप्लिकेशन, सर्वर पर डेटा के लिए बार-बार पोल करता है. इस तकनीक ज़्यादातर AJAX ऐप्लिकेशनों में इसका इस्तेमाल किया जाता है. एचटीटीपी प्रोटोकॉल के साथ, फ़ेच किए जा रहे हैं डेटा, अनुरोध और रिस्पॉन्स के फ़ॉर्मैट में होता है. क्लाइंट अनुरोध करता है और डेटा के साथ सर्वर के जवाब का इंतज़ार करता है. अगर कोई विकल्प उपलब्ध नहीं है, तो एक खाली जवाब दिया गया. ज़्यादा पोल कराने से एचटीटीपी ओवरहेड बढ़ता है.

  • लंबी अवधि के लिए पोल करना (हैंगिंग जीईटी / सीओईटी): अगर सर्वर में डेटा नहीं है उपलब्ध न होने पर, सर्वर अनुरोध को तब तक खुला रखता है, जब तक नया डेटा उपलब्ध नहीं हो जाता. इसलिए, इस तकनीक को अक्सर "हैंगिंग जीईटी" कहा जाता है. टास्क कब शुरू होगा जानकारी उपलब्ध होने पर, सर्वर जवाब देता है, कनेक्शन बंद कर देता है, साथ ही, यह प्रक्रिया दोहराई जाती है. इसलिए, सर्वर लगातार नया डेटा है. इसे सेट अप करने के लिए, डेवलपर आम तौर पर, Chrome का इस्तेमाल करके स्क्रिप्ट टैग एक 'असीमित' के रूप में iframe.

सर्वर से भेजे गए इवेंट, शुरुआत से ही कुशल होने के लिए डिज़ाइन किए गए हैं. एसएसई से संपर्क करते समय, सर्वर आपके ऐप्लिकेशन को अनुमति दें, वह भी बिना किसी शुरुआती अनुरोध के. दूसरे शब्दों में, अपडेट होने के बाद, उन्हें सर्वर से क्लाइंट तक स्ट्रीम किया जा सकता है. एसएसई सर्वर और क्लाइंट के बीच एक सिंगल-डायरेक्शनल चैनल खोल सकते हैं.

सर्वर से भेजे गए इवेंट और लंबी अवधि वाले पोल में मुख्य अंतर यह है कि एसएसई इन्हें सीधे ब्राउज़र मैनेज करता है और उपयोगकर्ता को सिर्फ़ मैसेज सुनना होता है.

सर्वर से भेजे गए इवेंट बनाम WebSockets

आपको WebSockets पर सर्वर से भेजे गए इवेंट क्यों चुनने चाहिए? यह अच्छा सवाल है.

WebSockets में ऐसा रिच प्रोटोकॉल है जो द्वि-दिशात्मक, फ़ुल-डूप्लेक्स संचार. दो-तरफ़ा चैनल बनाना बेहतर है जैसे कि गेम, मैसेजिंग ऐप्लिकेशन, और इस्तेमाल का कोई भी उदाहरण, जहां आपको करीब-करीब रीयल-टाइम अपडेट की ज़रूरत हो दोनों दिशाओं में.

हालांकि, कभी-कभी आपको सर्वर से सिर्फ़ एकतरफ़ा बातचीत की ज़रूरत होती है. उदाहरण के लिए, जब कोई दोस्त अपनी स्थिति अपडेट करता है, तो स्टॉक टिकर, न्यूज़ फ़ीड या अन्य ऑटोमेटेड डेटा पुश के तरीकों के बारे में ज़्यादा जानें. दूसरे शब्दों में, क्लाइंट-साइड वेब एसक्यूएल डेटाबेस या IndexedDB ऑब्जेक्ट स्टोर के लिए अपडेट. अगर आपको किसी सर्वर पर डेटा भेजना है, तो XMLHttpRequest हमेशा आपका दोस्त होता है.

एसएसई, एचटीटीपी पर भेजे जाते हैं. किसी खास प्रोटोकॉल या सर्वर की ज़रूरत नहीं होती लागू करने के लिए कहा जाता है. WebSockets को फ़ुल-डूप्लेक्स की ज़रूरत है और नए WebSocket सर्वर पर काम करता है.

इसके अलावा, सर्वर से भेजे गए इवेंट में ऐसी कई सुविधाएं होती हैं जो WebSockets में नहीं हैं डिज़ाइन के हिसाब से. इसमें अपने-आप फिर से कनेक्ट होना, इवेंट आईडी, और मैसेज भेजने की सुविधा शामिल है आर्बिट्रेरी इवेंट.

JavaScript के साथ EventSource बनाएं

किसी इवेंट स्ट्रीम की सदस्यता लेने के लिए, EventSource ऑब्जेक्ट बनाएं और उसे आपकी स्ट्रीम का यूआरएल:

const source = new EventSource('stream.php');

इसके बाद, message इवेंट के लिए हैंडलर सेट अप करें. आपके पास विकल्प के तौर पर open और error को सुनें:

source.addEventListener('message', (e) => {
  console.log(e.data);
});

source.addEventListener('open', (e) => {
  // Connection was opened.
});

source.addEventListener('error', (e) => {
  if (e.readyState == EventSource.CLOSED) {
    // Connection was closed.
  }
});

जब सर्वर से अपडेट पुश किए जाते हैं, तो onmessage हैंडलर ट्रिगर हो जाता है और इसकी e.data प्रॉपर्टी में नया डेटा उपलब्ध होगा. जादुई हिस्सा है कि जब भी कनेक्शन बंद किया जाता है, तो ब्राउज़र अपने आप सोर्स को ~3 सेकंड तक बदल देगा. लागू करने के लिए आपके सर्वर का कंट्रोल इस फिर से कनेक्ट होने का समय खत्म हो गया है.

हो गया. आपका क्लाइंट अब stream.php के इवेंट प्रोसेस कर सकता है.

इवेंट स्ट्रीम का फ़ॉर्मैट

स्रोत से कोई इवेंट स्ट्रीम भेजना सादा टेक्स्ट वाला जवाब, जिसे text/event-stream कॉन्टेंट टाइप के साथ दिखाया जाता है, जो SSE फ़ॉर्मैट में होना चाहिए. मूल रूप में, जवाब में एक data: लाइन होनी चाहिए. इसके बाद, संदेश, उसके बाद दो "\n" वर्ण:

data: My message\n\n

मल्टी-लाइन डेटा

अगर आपका मैसेज लंबा है, तो एक से ज़्यादा data: लाइनों का इस्तेमाल करके, मैसेज को बांटा जा सकता है. data: से शुरू होने वाली दो या उससे ज़्यादा लगातार आने वाली लाइनों को डेटा के एक हिस्से का मतलब है, सिर्फ़ एक message इवेंट ट्रिगर होता है.

हर लाइन एक "\n" पर खत्म होनी चाहिए (आखिरी को छोड़कर, जो खत्म होनी चाहिए दो के साथ). आपके message हैंडलर को भेजा गया नतीजा एक स्ट्रिंग होती है न्यूलाइन वर्णों से जोड़ा गया है. उदाहरण के लिए:

data: first line\n
data: second line\n\n</pre>

इससे "पहली लाइन\nदूसरी लाइन" बनती है e.data में. तब, कोई इनका इस्तेमाल कर सकता है संदेश "\n" को फिर से बनाने के लिए e.data.split('\n').join('') वर्ण

JSON डेटा भेजें

एक से ज़्यादा लाइनों का इस्तेमाल करने पर, सिंटैक्स का उल्लंघन किए बिना JSON को भेजने में मदद मिलती है:

data: {\n
data: "msg": "hello world",\n
data: "id": 12345\n
data: }\n\n

स्ट्रीम को मैनेज करने के लिए, संभावित क्लाइंट-साइड कोड भी दिया जा सकता है:

source.addEventListener('message', (e) => {
  const data = JSON.parse(e.data);
  console.log(data.id, data.msg);
});

किसी इवेंट के साथ आईडी जोड़ना

किसी स्ट्रीम इवेंट के लिए यूनीक आईडी भेजा जा सकता है. इसके लिए, इससे शुरू होने वाली लाइन शामिल करें id::

id: 12345\n
data: GOOG\n
data: 556\n\n

एक आईडी सेट करने से ब्राउज़र आखिरी इवेंट को इस तरह से ट्रैक कर सकता है कि अगर, सर्वर से कनेक्शन टूट गया है, एक विशेष HTTP हेडर (Last-Event-ID) है नए अनुरोध के साथ सेट करें. इससे ब्राउज़र यह तय कर पाता है कि कौनसा इवेंट ट्रिगर किया जा सकता है. message इवेंट में एक e.lastEventId प्रॉपर्टी है.

फिर से कनेक्ट करने के टाइमआउट को कंट्रोल करें

ब्राउज़र, सोर्स से करीब तीन सेकंड तक फिर से कनेक्ट करने की कोशिश करता है को चालू करें. इस टाइम आउट को बदलने के लिए, retry: से शुरू होने वाली लाइन और इसके बाद मिलीसेकंड की संख्या फिर से कनेक्ट करने का प्रयास करने से पहले इंतज़ार करने के लिए.

यहां दिए गए उदाहरण में, 10 सेकंड के बाद फिर से कनेक्ट करने की कोशिश की गई है:

retry: 10000\n
data: hello world\n\n

इवेंट का कोई नाम डालें

कोई एक इवेंट सोर्स, अलग-अलग टाइप के इवेंट जनरेट कर सकता है. इसके लिए, इवेंट का नाम. अगर event: से शुरू होने वाली कोई पंक्ति मौजूद हो, अगर इवेंट को कोई यूनीक नाम देना है, तो इवेंट उस नाम से जुड़ा होता है. क्लाइंट पर, उस खास इवेंट को सुनने के लिए इवेंट लिसनर को सेटअप किया जा सकता है.

उदाहरण के लिए, नीचे दिया गया सर्वर आउटपुट तीन तरह के इवेंट भेजता है, सामान्य 'मैसेज' इवेंट, 'userlogon', और 'update' इवेंट:

data: {"msg": "First message"}\n\n
event: userlogon\n
data: {"username": "John123"}\n\n
event: update\n
data: {"username": "John123", "emotion": "happy"}\n\n

क्लाइंट पर इवेंट लिसनर सेटअप करने के साथ:

source.addEventListener('message', (e) => {
  const data = JSON.parse(e.data);
  console.log(data.msg);
});

source.addEventListener('userlogon', (e) => {
  const data = JSON.parse(e.data);
  console.log(`User login: ${data.username}`);
});

source.addEventListener('update', (e) => {
  const data = JSON.parse(e.data);
  console.log(`${data.username} is now ${data.emotion}`);
};

सर्वर के उदाहरण

PHP में सर्वर को बुनियादी तौर पर लागू करने के बारे में यहां बताया गया है:

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.

/**
* Constructs the SSE data format and flushes that data to the client.
*
* @param string $id Timestamp/id of this connection.
* @param string $msg Line of text that should be transmitted.
**/

function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}

$serverTime = time();

sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
?>

यहां नोड JS पर एक मिलता-जुलता तरीका लागू किया गया है. इसके लिए, एक्सप्रेस हैंडलर:

app.get('/events', (req, res) => {
    // Send the SSE header.
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    // Sends an event to the client where the data is the current date,
    // then schedules the event to happen again after 5 seconds.
    const sendEvent = () => {
        const data = (new Date()).toLocaleTimeString();
        res.write("data: " + data + '\n\n');
        setTimeout(sendEvent, 5000);
    };

    // Send the initial event immediately.
    sendEvent();
});

sse-node.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <script>
    const source = new EventSource('/events');
    source.onmessage = (e) => {
        const content = document.createElement('div');
        content.textContent = e.data;
        document.body.append(content);
    };
    </script>
  </body>
</html>

इवेंट की स्ट्रीमिंग रद्द करना

आम तौर पर, कनेक्शन होने पर ब्राउज़र, इवेंट सोर्स से अपने-आप फिर से कनेक्ट हो जाता है बंद है, लेकिन इस व्यवहार को क्लाइंट या सर्वर से रद्द किया जा सकता है.

क्लाइंट की ओर से की जा रही स्ट्रीम को रद्द करने के लिए, इस पर कॉल करें:

source.close();

सर्वर से किसी स्ट्रीम को रद्द करने के लिए, text/event-stream के बजाय किसी अन्य सोर्स का इस्तेमाल करें Content-Type या 200 OK के अलावा कोई अन्य एचटीटीपी स्टेटस दिखाएं (जैसे कि 404 Not Found).

दोनों ही तरीके, ब्राउज़र को फिर से कनेक्ट करने से रोकते हैं.

सुरक्षा के बारे में जानकारी

EventSource से जनरेट किए गए अनुरोध एक ही ऑरिजिन से जुड़ी नीतियों के तहत आते हैं फ़ेच जैसे दूसरे नेटवर्क एपीआई. अगर आपको अपने सर्वर पर एसएसई एंडपॉइंट की ज़रूरत है, तो अलग-अलग ऑरिजिन से ऐक्सेस किया जा सकता है. साथ ही, अफ़िलिएट प्रोग्राम को चालू करने का तरीका क्रॉस ऑरिजिन रिसॉर्स शेयरिंग (सीओआरएस).