サヌバヌ送信むベントを䜿甚したリアルタむムアプリケヌションの䜜成

぀い最近、 Firefox 6がSSE Opera 10.6 +、Chrome、WebKit 5 +、iOS Safari 4 +、Opera Mobile 10+で既に利甚可胜 を受信するこずがわかったため、すべおのブラりザヌの半分以䞊ナヌザヌにリヌチするのサポヌトはもはや有利ではありたせん山。 この技術を詳しく芋おみたしょう。 SSEは7幎以䞊前にIan Hickson によっお提䟛されたしたが、1幎前にブラりザに衚瀺されるようになりたした。 WebSocketがあるのに、なぜ別のプロトコルが必芁なのですか しかし、すべおに長所ず短所がありたす。SSEが䜕に圹立぀か芋おみたしょう。

SSEの抂念は単玔です。クラむアントはサヌバヌむベントにサブスクラむブし、むベントが発生するずすぐに、クラむアントは通知ずこのむベントに関連するデヌタをすぐに受信したす。 SSEプロトコルの有甚性を理解するには、むベントを受信するための通垞の方法ず比范する必芁がありたす。その本質を簡単に説明したす。

ポヌリング


最も単玔ですが、最も効果的な方法ではありたせん。クラむアントは数秒ごずにむベントを求めおサヌバヌをポヌリングしたす。 䜕も存圚しない堎合でも、クラむアントはすべお同じように芁求を行いたすが、䜕が来るかわかりたせん。
長所
-ただ
-デヌタを振るこずができたす
短所
-倚くの远加リク゚スト
-むベントは垞に遅れる
-サヌバヌは、クラむアントがむベントを取埗するか、むベントが期限切れになるたでむベントを保存する必芁がありたす

長いポヌリング


以前の方法の改良版。 クラむアントはサヌバヌにリク゚ストを送信し、サヌバヌはデヌタが到着するかクラむアントが自動的に切断するたで接続を開いたたたにしたす。 デヌタが到着するずすぐに、応答が送信され、接続が閉じられ、次の接続が開かれたす。
ポヌリングず比范した長所
-リク゚ストの最小数
-むベントの高い時間的粟床
-サヌバヌは再接続䞭にのみむベントを保存したす
ポヌリングず比范した短所
-より耇雑なスキヌム

Websocket


これは、クラむアントずサヌバヌが同等の条件で通信できるようにするバむナリ二重プロトコルです。 このプロトコルは、ゲヌム、チャット、およびリアルタむムに近い非垞に正確なむベントが必芁なすべおのアプリケヌションに䜿甚できたす。
ロングポヌリングず比范した長所
-1぀の接続が䞊昇する
-むベントの非垞に高い時間的粟床
-ブラりザヌによっお制埡されるネットワヌク障害管理
ロングポヌリングず比范した短所
-HTTPは互換性のあるプロトコルではないため、独自のサヌバヌが必芁です。デバッグは耇雑です

私たちはこのような矎しいWebSocketプロトコルを持っおいるので、なぜSSEを䜿甚する必芁がありたすか たず、すべおのWebアプリケヌションが双方向通信を必芁ずするわけではありたせん-SSEも適しおいたす。 次に、SSEはHTTP互換プロトコルであり、任意のWebサヌバヌでむベントブロヌドキャストを実装できたす。

サヌバヌ送信むベントプロトコル


クラむアントはサヌバヌに芁求を送信し、サヌバヌは応答ずしお次のヘッダヌを送信したす。
Content-Type: text/event-stream 

たた、接続を閉じたせんphpでは、無限ルヌプを䜜成できたす。node.jsで行う方法に぀いおは、サンプル蚘事で説明したす。 それだけです-SSEは動䜜したす クラむアントにデヌタを送信するために、サヌバヌは次の圢匏の行を゜ケットに曞き蟌むだけです。
 data: My message\n\n 

耇数行のデヌタを送信する必芁がある堎合、圢匏は次のようになりたす。
 data: {\n data: "msg": "hello world",\n data: "id": 12345\n data: }\n\n 

ここでは、原則ずしお、プロトコルベヌス党䜓です。 さらに、サヌバヌはメッセヌゞIDを送信できたす。これは、接続が切断された堎合に必芁です。 接続が切断された堎合、クラむアントは接続しようずするず、特別なヘッダヌLast-Event-IDを送信しお、倱われたむベントを埩元したす。
 id: 12345\n data: GOOG\n data: 556\n\n 

゚ラヌが発生した堎合の再詊行時間
 retry: 10000\n data: hello world\n\n 

idおよびretryフィヌルドはオプションです。

クラむアントでは、すべおが次のようになりたす。
 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


マルチパヌトストリヌミングずも呌ばれたすFirefoxのみをサポヌト。 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むンタラクティブで特別なreadyStateをサポヌトする必芁がありたす-このステヌタスは、デヌタの䞀郚が到着したが、接続がただ閉じられおいないこずを瀺したす。 コヌド3でreadyStateを䜿甚するjQuery には同じ名前のプラグむンがありたす。たた、い぀ものように、すべおのブラりザヌがコヌド3でreadyStateをサポヌトしおいるわけではありたせん。

䟋サヌバヌ送信むベントでのチャット


SSEに関する䞀連のむベントオフラむンになる、オンラむンになる、メッセヌゞを受け入れたす。 なぜなら SSEはメッセヌゞを送信できないため、HTTPを介しお送信したす。

䜜業のスキヌムは次のずおりです。
-チャットに入るずき、名前が芁求されたす
-クラむアントはチャットサヌバヌに接続したす。 むベントストリヌムが䜜成されたす。
-クラむアントが接続するず、チャットは党員にむベントを送信したすusernameonline
-クラむアントを切断するず、チャットは党員にむベントを送信したすusernameoffline
-クラむアントはHTTP「POST /メッセヌゞ」を介しおメッセヌゞを送信でき、サヌバヌはこのメッセヌゞを受信し、SSEを介しおすべおのクラむアントに受信したメッセヌゞを送信したす。

クラむアントコヌドを分析したしょう。 䞀郚のブラりザで$ .readyの代わりに無限のダりンロヌドが行われないように、setTimeoutを実行したす。
 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); 

renderMessageメ゜ッドずペヌゞレむアりトは考慮したせん。 すべおのクラむアントコヌドは、 index.htmlにありたす。

サヌバヌ偎には、Node.jsがありたす。 ここではすべおがより耇雑ですが、1人のナヌザヌからすべおのナヌザヌぞのマルチキャストメッセヌゞの䞻な難しさであり、SSEを介した通信の構築は困難です。

必芁なモゞュヌルを接続したす

 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); } 

private _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メ゜ッドは、パブリックブロヌドキャストメ゜ッドずナニキャストメ゜ッドを䜿甚しお、それぞれすべおのクラむアントず1぀のクラむアントにメッセヌゞを送信したす。

サヌバヌを䜜成しおオンにしたす

 //   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 

Firefox 6、Opera 10.6以降、Chrome、WebKit 5以降、iOS Safari 4以降、Opera Mobile 10以降のいずれかのブラりザヌを開きたす。 http:// localhost/にアクセスしおチャットしおください

おわりに


SSEはLong Polingに取っお代わる優れたテクノロゞヌであり、シンプルであり、WebSocketsほど効果的ではありたせん。 珟圚、SSEはOpera 10.6+Opera 9は叀いSSE暙準をサポヌトしおいたす、Chrome、Safari 5+をサポヌトしおいたす。 FirefoxはMultipart XMLHTTPRequestをサポヌトしおいたす。このためにラッパヌを䜜成し、SSEむンタヌフェヌスずしお䜿甚できたす。

参照資料


1. SSEチャットのオンラむン䟋は、 sse-chat.nodester.comで芋るこずができたす。
Nodesterプロキシの機胜により、これはチャットの䞀郚が切り捚おられたバヌゞョンですオンラむンのナヌザヌ数に関するメッセヌゞはなく、チャットから退出するずいうメッセヌゞもありたせん。頻繁に再接続される可胜性がありたす
2. ゜ヌスの䟋 github.com/azproduction/event-source-chat
3. 別のSSEチュヌトリアル
4. 仕様

PSチャットはhabraeffectをカバヌしおいるように芋えたすが、nodesterで䜕かをしおいるようです倚くの堎合、このようになりたす。 結果に興味がある堎合は、GitHubから゜ヌスをダりンロヌドしおください。

マルチパヌトXMLHTTPRequest、XMLHTTPRequestによりUPDが远加されたした yui_room9を远加しおくれたむンタラクティブな感謝

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


All Articles