सर्वर से भेजे गए घटनाओं के साथ वास्तविक समय अनुप्रयोग बनाना

अभी हाल ही में यह ज्ञात हुआ कि फ़ायरफ़ॉक्स 6 को एसएसई ( पहले से ही ओपेरा 10.6+, क्रोम, वेबकेट 5+, आईओएस सफारी 4+, ओपेरा मोबाइल 10+) में मिलेगा , ताकि आधे से अधिक सभी ब्राउज़रों (उपयोगकर्ताओं तक पहुँचने) का समर्थन अब पक्ष में न हो पहाड़। इस तकनीक पर करीब से नजर डालने का समय आ गया है। SSE को इयान हिकसन द्वारा 7 साल पहले पेश किया गया था, लेकिन केवल एक साल पहले यह ब्राउज़रों में दिखाई देने लगा। हमारे पास WebSockets है, हमें दूसरे प्रोटोकॉल की आवश्यकता क्यों है? लेकिन हर चीज के अपने पेशेवरों और विपक्ष हैं, आइए देखें कि एसएसई के लिए क्या उपयोगी हो सकता है।

SSE का विचार सरल है - क्लाइंट सर्वर घटनाओं की सदस्यता लेता है और जैसे ही घटना होती है, ग्राहक को तुरंत एक अधिसूचना और इस घटना से संबंधित कुछ डेटा प्राप्त होता है। एसएसई प्रोटोकॉल की उपयोगिता को समझने के लिए, आपको घटनाओं को प्राप्त करने के लिए सामान्य तरीकों के साथ तुलना करने की आवश्यकता है, मैं संक्षेप में उनके विवरण समझाऊंगा:

मतदान


सबसे सरल, लेकिन सबसे प्रभावी नहीं, विधि: क्लाइंट घटनाओं के लिए हर कुछ सेकंड में एक बार सर्वर को पोल करता है। यहां तक ​​कि अगर कुछ भी नहीं है, तो क्लाइंट सभी एक ही अनुरोध करता है - लेकिन आप कभी नहीं जानते कि क्या आएगा।
पेशेवरों:
- बस
- डेटा को हिलाया जा सकता है
विपक्ष:
- बहुत सारे अतिरिक्त अनुरोध
- कार्यक्रम हमेशा देरी से आते हैं
- सर्वर को इवेंट्स को तब तक स्टोर करना होता है जब तक क्लाइंट उन्हें पिक नहीं करता या जब तक वे आउट ऑफ डेट नहीं हो जाते

लंबा मतदान


पिछले पद्धति का एक उन्नत संस्करण। क्लाइंट सर्वर को एक अनुरोध भेजता है, सर्वर तब तक कनेक्शन को खुला रखता है जब तक कि कुछ डेटा नहीं आता है या क्लाइंट अपने आप डिस्कनेक्ट हो जाता है। जैसे ही डेटा आ गया है, एक प्रतिक्रिया भेजी जाती है और कनेक्शन बंद हो जाता है और अगला खुल जाता है और इसी तरह।
मतदान की तुलना में पेशेवरों:
- अनुरोधों की न्यूनतम संख्या
- घटनाओं की उच्च अस्थायी सटीकता
- सर्वर केवल पुन: कनेक्ट होने के दौरान घटनाओं को संग्रहीत करता है
मतदान की तुलना में विपक्ष:
- अधिक जटिल योजना

WebSockets


यह एक द्विआधारी द्वैध प्रोटोकॉल है जो क्लाइंट और सर्वर को समान शर्तों पर संवाद करने की अनुमति देता है। यह प्रोटोकॉल गेम, चैट और उन सभी एप्लिकेशन के लिए उपयोग किया जा सकता है, जहां आपको वास्तविक समय के करीब बेहद सटीक घटनाओं की आवश्यकता होती है।
लंबे मतदान के मुकाबले पेशेवरों:
- एक कनेक्शन बढ़ जाता है
- घटनाओं की अत्यधिक उच्च अस्थायी सटीकता
- नेटवर्क गलती प्रबंधन ब्राउज़र द्वारा नियंत्रित
लंबे मतदान की तुलना में विपक्ष:
- HTTP एक संगत प्रोटोकॉल नहीं है, आपको अपना सर्वर चाहिए, डीबगिंग जटिल है

तो आप एसएसई का उपयोग क्यों करें, क्योंकि हमारे पास इस तरह के एक सुंदर वेबसोकेट प्रोटोकॉल है! " सबसे पहले, हर वेब एप्लिकेशन को दो-तरफ़ा संचार की आवश्यकता नहीं है - एसएसई भी उपयुक्त है। दूसरे, एसएसई एक HTTP संगत प्रोटोकॉल है और आप किसी भी वेब सर्वर पर ईवेंट प्रसारण को लागू कर सकते हैं।

सर्वर-भेजे गए इवेंट प्रोटोकॉल


क्लाइंट सर्वर के लिए एक अनुरोध भेजता है, सर्वर प्रतिक्रिया में निम्नलिखित हेडर भेजता है:
Content-Type: text/event-stream 

और यह कनेक्शन बंद नहीं करता (php पर आप एक अंतहीन लूप बना सकते हैं, नोड पर कैसे करें। उदाहरण के लेख में बताया जाएगा)। यह बात है - SSE काम करता है! क्लाइंट को कुछ डेटा भेजने के लिए, सर्वर केवल सॉकेट के लिए निम्न प्रारूप की एक पंक्ति लिखता है:
 data: My message\n\n 

यदि आपको डेटा की कई पंक्तियाँ भेजने की आवश्यकता है, तो प्रारूप इस प्रकार होगा:
 data: {\n data: "msg": "hello world",\n data: "id": 12345\n data: }\n\n 

यहां, सिद्धांत रूप में, और संपूर्ण प्रोटोकॉल आधार। इसके अलावा, सर्वर संदेश आईडी भेज सकता है यह उस स्थिति में आवश्यक है जब कनेक्शन काट दिया गया था। यदि कनेक्शन गिरा दिया गया था, तो कनेक्ट करने का प्रयास करने वाला क्लाइंट, खोए हुए को पुनर्स्थापित करने के लिए एक विशेष हेडर (लास्ट-इवेंट-आईडी) भेजेगा:
 id: 12345\n data: GOOG\n data: 556\n\n 

त्रुटियों के मामले में समय निकालें:
 retry: 10000\n data: hello world\n\n 

आईडी और रिट्री फ़ील्ड वैकल्पिक हैं।

क्लाइंट पर, सब कुछ इस तरह दिखेगा:
 var source = new EventSource('http://localhost/stream.php'); source.addEventListener('message', function(e) { //  -  console.log(e.data); }, false); source.addEventListener('open', function(e) { //    }, false); source.addEventListener('error', function(e) { if (e.eventPhase == EventSource.CLOSED) { //   } }, false); 

सब कुछ बेहद सरल है। आइए SSE प्रोटोकॉल के आधार पर एक एप्लिकेशन बनाएं। हमेशा की तरह, यह एक चैट होगी।

XMLHTTPRequest मल्टीपार्ट करें


जिसे मल्टीपार्ट स्ट्रीमिंग कहा जाता है (केवल फ़ायरफ़ॉक्स का समर्थन करता है)। SSE प्रोटोकॉल के समान।
इसके हेडर में एक प्रारूप है:
 Content-type: multipart/x-mixed-replace;boundary=smthing 

और भागों को इस प्रारूप में भेजा जाता है:
 Content-type: text/html\r\n\r\n --smthing\n Message\n --smthing\n 


क्लाइंट में एक नियमित XHR बनाया जाता है, लेकिन अनुरोध भेजने से पहले, आपको झंडा req.multipart = true; सेट करना होगा req.multipart = true;
क्या यह SSE जैसा दिखता है? अधिक जानकारी

एक और प्रोटोकॉल है जो SSE को जन्म दे सकता है:

XMLHTTPRequest: इंटरएक्टिव


इसका उपयोग करने के लिए, कोड 3 (इंटरैक्टिव) के साथ विशेष रेडीस्टेट के लिए ब्राउज़र समर्थन की आवश्यकता है - यह स्थिति इंगित करती है कि डेटा का हिस्सा आ गया है, लेकिन कनेक्शन अभी तक बंद नहीं हुआ है। कोड 3 के साथ रेडीस्टेट का उपयोग करके jQuery के लिए एक ही नाम का एक प्लगइन है । और हमेशा की तरह, सभी ब्राउज़र कोड 3 के साथ रेडीस्टेट का समर्थन नहीं करते हैं।

उदाहरण: सर्वर-भेजे गए ईवेंट पर चैट करें


हम SSE पर होने वाली घटनाओं की धारा को स्वीकार करेंगे: ऑफ़लाइन होने, ऑनलाइन आने, संदेश। क्योंकि SSE संदेश नहीं भेज सकता है, तो हम उन्हें HTTP के माध्यम से भेजेंगे।

कार्य की योजना इस प्रकार है:
- चैट में प्रवेश करते समय, एक नाम का अनुरोध किया जाता है
- क्लाइंट चैट सर्वर से कनेक्ट होता है। एक इवेंट स्ट्रीम बनाया जाता है।
- जब कोई ग्राहक जुड़ता है, तो चैट सभी को एक घटना भेजता है:% उपयोगकर्ता नाम% ऑनलाइन
- जब आप क्लाइंट को डिस्कनेक्ट करते हैं, तो चैट सभी को एक घटना भेजता है:% उपयोगकर्ता नाम ऑफ़लाइन
- क्लाइंट HTTP "POST / संदेश" के माध्यम से संदेश भेज सकता है। सर्वर इस संदेश को प्राप्त करता है और प्राप्त संदेश को सभी ग्राहकों को SSE के माध्यम से भेजता है।

ग्राहक कोड का विश्लेषण करते हैं। यह सुनिश्चित करने के लिए कि कुछ ब्राउज़रों के पास पहले से $ के बजाय अंतहीन डाउनलोड नहीं हैं, हम सेटटाइमआउट को निष्पादित करते हैं:
 setTimeout(function () { //   ,   $.ready       }, 50); 

अनुरोध उपयोगकर्ता नाम:
 //    localStorage    var name = (prompt('Name:', window.localStorage ? window.localStorage['name'] || '' : '') || 'anonymous').substr(0, 20); //    if (window.localStorage) { window.localStorage['name'] = name; } 

हम एक EventSource बनाते हैं और इसे उपयोगकर्ता नाम (अब उपयोगकर्ता ऑनलाइन है) पास करते हैं और आवश्यक घटनाओं को सुनते हैं:
 var eventSrc = new EventSource("/event?name=" + name); //   EventSource - "message" eventSrc.addEventListener("message", function(event) { var data = JSON.parse(event.data); //      renderMessage(data); }, false); //   EventSource - "error" eventSrc.addEventListener("error", function(event) { //      renderMessage({ isbot: true, message: 'connection error', name: '@Chat' }); }, false); 

मैं रेंडरमैसेज पद्धति और पेज लेआउट पर विचार नहीं करूंगा। सभी क्लाइंट कोड यहां पाए जा सकते हैं: index.html

सर्वर की तरफ, हमारे पास Node.js. यहां सब कुछ अधिक जटिल है, लेकिन एक उपयोगकर्ता से सभी के लिए मल्टीकास्ट संदेशों में मुख्य कठिनाई है, और एसएसई पर संचार के निर्माण में नहीं।

हम आवश्यक मॉड्यूल कनेक्ट करते हैं

 var http = require('http'), fs = require('fs'), qs = require('querystring'), parse = require('url').parse; //   (index.html      Node.js) var indexFile = fs.readFileSync('index.html'); // Buffer 

रोथ

हम रूट मार्गों की एक सूची बनाते हैं, जिसमें निम्नलिखित वस्तुएं शामिल हैं:
1. स्टेटिक्स। इंडेक्स पेज, हम सिर्फ हेलमेट स्टेटिक्स:
  'GET /': function (request, response) { //    response.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'}); response.write(indexFile); response.end(); } 

2. SSE कनेक्शन उठाना:
  'GET /event': function (request, response) { var url = parse(request.url, true); var name = (url.query.name || 'anonymous').substr(0, 20); var clientId = Clients.generateClientId(); //     EventSource response.writeHead(200, {'Content-Type': 'text/event-stream'}); //     ,     2  request.socket.setTimeout(1000 * 60 * 60); // 1  //    -    request.on('close', function () { Clients.remove(clientId); }); //     Clients.add(clientId, response, name); } 

3. ग्राहक से संदेश:
  'POST /message': function (request, response) { var data = ''; //    POST request.on('data', function (chunk) { data += chunk; }); //   POST   request.on('end', function () { //   data = qs.parse(data); //    Clients.broadcast(data.message, data.name, false); response.writeHead(200); response.end(); }); } 

4. डिफ़ॉल्ट रूट - पृष्ठ 404:
  $: function (request, response) { response.writeHead(404); response.end(); } 

ग्राहक प्रबंधक - ग्राहक

एक नया ग्राहक जोड़ते समय (जोड़), प्रबंधक ग्राहक को आए सभी संदेश भेजता है:
  add: function (clientId, response, name) { this._clients[clientId] = {response: response, name: name || 'anonymous'}; this.count++; //      this.unicast(clientId, 'Hello, ' + name + '! Online ' + this.count, '@ChatBot', true); this.broadcast(name + ' online', '@ChatBot', true); } 

हटाते समय, यह कनेक्शन बंद कर देता है और सभी को भेजता है कि ग्राहक ऑफ़लाइन है:
  remove: function (clientId) { //   ,     var client = this._clients[clientId]; if (!client) { return; } //   client.response.end(); //   delete this._clients[clientId]; this.count--; //   ,    //      this.broadcast(client.name + ' offline', '@ChatBot', true); } 

निजी _send विधि का उपयोग ग्राहकों को संदेश भेजने के लिए किया जाता है:
  _send: function (clients, message, name, isbot) { if (!message || !name) { return; } //   var data = JSON.stringify({ message: message.substr(0, 1000), name: (name || 'anonymous').substr(0, 20), isbot: isbot || false }); //   ,      //     -   Node.js data = new Buffer("data: " + data + "\n\n", 'utf8'); //   SSE //   clients.forEach(function (client) { client.response.write(data); //   }); } 

_Send विधि सभी और एक ग्राहक को क्रमशः संदेश भेजने के लिए सार्वजनिक प्रसारण और यूनिकस्ट विधियों का उपयोग करती है।

हम सर्वर बनाते हैं और चालू करते हैं

 //   var httpServer = http.createServer(function (request, response) { var key = request.method + ' ' + parse(request.url).pathname; //   ,     Routes.$ - 404 (Routes[key] || Routes.$)(request, response); }); //   httpServer.listen(80); console.log('Online'); 

स्रोत कोड server.js

SSE पर हमारी चैट तैयार है। हम सर्वर शुरू करते हैं:
 $ node server.js 

ब्राउज़र में से एक खोलें: फ़ायरफ़ॉक्स 6, ओपेरा 10.6+, क्रोम, वेबिट 5+, आईओएस सफारी 4+, ओपेरा मोबाइल 10+। http:// localhost/ और चैट पर जाएँ!

निष्कर्ष


SSE एक अच्छी तकनीक है, जिसे Long Poling को विस्थापित करना चाहिए। यह सरल है और WebSockets की तुलना में कम प्रभावी नहीं है। अब एसएसई ओपेरा 10.6+ (ओपेरा 9 पुराने एसएसई मानक का समर्थन करता है), क्रोम, सफारी 5+ का समर्थन करता है। फ़ायरफ़ॉक्स मल्टीपार्ट XMLHTTPRequest का समर्थन करता है, जिसके लिए आप एक आवरण लिख सकते हैं और इसे SSE इंटरफ़ेस के रूप में उपयोग कर सकते हैं।

संदर्भ


1. SSE चैट का ऑनलाइन उदाहरण यहां देखा जा सकता है: sse-chat.nodester.com
यह नोडस्टर प्रॉक्सिंग की विशेषताओं के कारण चैट का कुछ हद तक छोटा संस्करण है (ऑनलाइन उपयोगकर्ताओं की संख्या के बारे में कोई संदेश नहीं है और चैट छोड़ने के बारे में कोई संदेश नहीं है, अक्सर पुन: कनेक्ट हो सकता है)
2. उदाहरण स्रोत : github.com/azproduction/event-source-chat
3. एक और एसएसई ट्यूटोरियल
4. विशिष्टता

पीएस ऐसा लगता है कि चैट ने हेबरफेक्ट को कवर किया है, लेकिन शायद नोडस्टर के साथ कुछ (यह अक्सर ऐसा होता है)। यदि आप परिणाम में रुचि रखते हैं, तो GitHub से स्रोत डाउनलोड करें।

UPD को Multipart XMLHTTPRequest, XMLHTTPRequest द्वारा जोड़ा गया: yui_n9 के लिए इंटरएक्टिव धन्यवाद

Source: https://habr.com/ru/post/In120429/


All Articles