HTML 5  stockage local& synchronisation Raphaël Rougeron   @goldoraf
Une mutation est en cours                            Pages HTML  Serveur          Client                             Singl...
Form validation                        GeolocationDrag n Drop API       Web Notifications                              Can...
Stockage local
1er bénéfice : le offline ?
1er bénéfice : économiser le         serveur !Source : HTML5 Web Sockets: A Quantum Leap in Scalability for the Webhttp://...
4 étapes
Etape 1   Rendre les ressourcesdisponibles hors connexion
Application Cache<html manifest="cache.manifest"> <html manifest="cache.manifest">      CACHE MANIFEST       CACHE MANIFES...
!Avoir un manifestechange complètementles règles daccès aux ressources
CACHE MANIFEST CACHE MANIFEST# v.1.2­build1234 # v.1.2­build1234CACHE: CACHE:index.html index.htmlcss/style.css css/style....
CACHE MANIFEST CACHE MANIFEST# v.1.2­build1234 # v.1.2­build1234CACHE: CACHE:index.html index.htmlcss/style.css css/style....
CACHE MANIFEST CACHE MANIFEST# v.1.2­build1234 # v.1.2­build1234CACHE: CACHE:index.html index.htmlcss/style.css css/style....
window.applicationCache     status     UNCACHED     IDLE     CHECKING     DOWNLOADING     UPDATEREADY     OBSOLETE
var appCache = window.applicationCache; var appCache = window.applicationCache;appCache.addEventListener(updateready, func...
>= 10 ?   >= 3.5   >= 4.0    >= 4.0     >= 10.6          >= 3.2    >= 2.1    >= 11.0
Etape 2Découpler lapplication du        réseau
var Todo = Backbone.Model.extend({ var Todo = Backbone.Model.extend({    toggle: function() {     toggle: function() {    ...
Backbone.Sync●   create()   POST   /collection●   read()     GET    /collection[/id]●   update()   PUT    /collection/id● ...
Etape 3Stocker localement les      données
3 options
FileSystem API  !    seulement
window.requestFileSystem  = window.requestFileSystem  window.requestFileSystem  = window.requestFileSystem                ...
FileErrorcodeQUOTA_EXCEEDED_ERRNOT_FOUND_ERRSECURITY_ERRINVALID_MODIFICATION_ERRINVALID_STATE_ERR
function onInitFs(fs) { function onInitFs(fs) {  fs.root.getFile(data.txt, {create: true}, function(entry) {   fs.root.get...
FileEntrynamefullpathisFileisDirectory…file()createWriter()moveTo()copyTo()remove()...
function persistData(fs, data) { function persistData(fs, data) {  fs.root.getFile(log.txt, {create: true}, function(entry...
Web StoragelocalStorage et sessionStorage
Un simple dépôt clé-valeurlocalStorage.setItem("foo", "bar"); localStorage.setItem("foo", "bar");localStorage.setItem("twe...
Quota de 5 Mo!   Pas de transactions    Pas dindexation
localStorage["tweets:1234"] = "Lorem ipsum..."; localStorage["tweets:1234"] = "Lorem ipsum...";localStorage["tweets:1235"]...
➔ Très bien supporté       ➔ Performances➔ API simple                  ➔ API synchrone➔ Mis en avant par de nb      ➔ Séri...
>= 8   >= 3.5   >= 4.0    >= 4.0     >= 10.5       >= 3.2    >= 2.1    >= 11.0
localStorage adapterBackbone.sync = function(method, model, options) { Backbone.sync = function(method, model, options) { ...
Petit intermède        Vous connaissez Redis ?BankersBoxvar bb = new BankersBox(1); var bb = new BankersBox(1);bb.set("foo...
IndexedDB
Préfixes, préfixes...window.indexedDB = window.indexedDB || window.mozIndexedDB ||      window.indexedDB = window.indexedD...
API asynchronefunction IndexedDBAdapter() { function IndexedDBAdapter() {   this.db = null;    this.db = null;      var re...
Création dun dépôtIndexedDBAdapter.prototype.create = function(storeName) { IndexedDBAdapter.prototype.create = function(s...
Persistence dun objetIndexedDBAdapter.prototype.save(storeName, object, callback)  IndexedDBAdapter.prototype.save(storeNa...
Requête simpleIndexedDBAdapter.prototype.all(storeName, callback) { IndexedDBAdapter.prototype.all(storeName, callback) { ...
Key RangesIDBKeyRange.upperBound(x);            // <= x IDBKeyRange.upperBound(x);            // <= xIDBKeyRange.upperBoun...
Indexvar contacts = [ var contacts = [    { firstname: "John", lastname: "Doe", email: "jdoe@zz.com" },     { firstname: "...
Indexvar contacts = [ var contacts = [    { firstname: "John", lastname: "Doe", email: "jdoe@zz.com" },     { firstname: "...
>= 10 ?   >= 8.0   >= 16.0   ?       ?            ?         ?          ?
Et pourquoi pasWeb SQL Database ?
X     X      >= 4.0    >= 3.1     >= 10.5    >= 3.2    >= 2.1    >= 11.0
Le polyfill ultime ?  Lawnchair  ● localStorage  ● indexedDB  ● webkit­sqlite  ● gears­sqlite  ● ie­userdata  ● backberry­...
Etape 4Développer une stratégie de     synchronisation
Lexemple dActiveSync
POST /Microsoft­Server­ActiveSync?User=jdoe@zz.com&...POST /Microsoft­Server­ActiveSync?User=jdoe@zz.com&...     &Cmd=<Com...
Cmd=Sync&SyncKey=123456789&FolderId=5678Cmd=Sync&SyncKey=123456789&FolderId=5678{{   syncKey: 987654321,   syncKey: 987654...
Autres pistesMozilla ServicesSyncMLOperational Transformation → ShareJS
Etape subsidiaire    Le "push"
PollingClient             Serveur                             n secondes                             n secondes
Long polling (COMET) Client       Serveur                  Événement                  côté serveur                  Événem...
WebSockets
HandshakeGET /resource name/ HTTP/1.1Upgrade: WebSocketConnection: UpgradeHost: /server/Origin: /origin/WebSocket­Protocol...
Serveur (avec node.js) var io = require(socket.io).listen(80); var io = require(socket.io).listen(80); io.sockets.on(conne...
>= 10 ?   >= 6.0   >= 14.0   >= 5.0     >= 11.0          >= 4.2      ?       >= 11.0
Avec Socket.IO>= 5.5   >= 3.0   >= 4.0    >= 3.0     >= 10.61         >= 3.2    >= 2.1    >= 11.0
Server-Sent Events
HTTP traditionnelGET /myAppStreamContent­Type: text/event­stream
if (window.EventSource) {if (window.EventSource) {  var source = new EventSource(myAppStream);  var source = new EventSour...
Format des messagesdata: Hello worldnnMulti-lignedata: Hellondata: worldnnJSONdata: {ndata: "msg": "Hello world",ndata: "i...
Format des messagesUtilisation des IDsid: 12345ndata: {"user": "goldo", "msg": "Hello !"}nnUtilisation des eventsdata: {"m...
AvantagesReconnexion automatiqueIdsEvents
Questions / réponses
Html5 : stockage local & synchronisation
Prochain SlideShare
Chargement dans…5
×

Html5 : stockage local & synchronisation

3 833 vues

Publié le

Publié dans : Technologie, Design
  • Soyez le premier à commenter

Html5 : stockage local & synchronisation

  1. 1. HTML 5 stockage local& synchronisation Raphaël Rougeron @goldoraf
  2. 2. Une mutation est en cours Pages HTML Serveur Client Single-Page Application JSON
  3. 3. Form validation GeolocationDrag n Drop API Web Notifications Canvas WebGL Video Audio File API Web Workers Web Sockets
  4. 4. Stockage local
  5. 5. 1er bénéfice : le offline ?
  6. 6. 1er bénéfice : économiser le serveur !Source : HTML5 Web Sockets: A Quantum Leap in Scalability for the Webhttp://soa.sys-con.com/node/1315473
  7. 7. 4 étapes
  8. 8. Etape 1 Rendre les ressourcesdisponibles hors connexion
  9. 9. Application Cache<html manifest="cache.manifest"> <html manifest="cache.manifest"> CACHE MANIFEST CACHE MANIFEST CACHE: CACHE: index.html index.html css/style.css css/style.css img/logo.png img/logo.png js/main.js js/main.js text/cache­manifest
  10. 10. !Avoir un manifestechange complètementles règles daccès aux ressources
  11. 11. CACHE MANIFEST CACHE MANIFEST# v.1.2­build1234 # v.1.2­build1234CACHE: CACHE:index.html index.htmlcss/style.css css/style.cssimg/logo.png img/logo.pngjs/main.js js/main.js
  12. 12. CACHE MANIFEST CACHE MANIFEST# v.1.2­build1234 # v.1.2­build1234CACHE: CACHE:index.html index.htmlcss/style.css css/style.cssimg/logo.png img/logo.pngjs/main.js js/main.jsNETWORK NETWORK/sync /sync/api/* /api/*http://api.twitter.com http://api.twitter.com
  13. 13. CACHE MANIFEST CACHE MANIFEST# v.1.2­build1234 # v.1.2­build1234CACHE: CACHE:index.html index.htmlcss/style.css css/style.cssimg/logo.png img/logo.pngjs/main.js js/main.jsNETWORK NETWORK/sync /sync/api/* /api/*http://api.twitter.com http://api.twitter.comFALLBACK FALLBACKimages/large/ images/offline.jpg images/large/ images/offline.jpg*.html /offline.html *.html /offline.html
  14. 14. window.applicationCache status UNCACHED IDLE CHECKING DOWNLOADING UPDATEREADY OBSOLETE
  15. 15. var appCache = window.applicationCache; var appCache = window.applicationCache;appCache.addEventListener(updateready, function(e) { appCache.addEventListener(updateready, function(e) {    if (appCache.status == appCache.UPDATEREADY) {     if (appCache.status == appCache.UPDATEREADY) {      appCache.swapCache();       appCache.swapCache();      if (confirm(Une nouvelle version est dispo)) {       if (confirm(Une nouvelle version est dispo)) {        window.location.reload();         window.location.reload();      }       }    }     }}, false); }, false);
  16. 16. >= 10 ? >= 3.5 >= 4.0 >= 4.0 >= 10.6 >= 3.2 >= 2.1 >= 11.0
  17. 17. Etape 2Découpler lapplication du réseau
  18. 18. var Todo = Backbone.Model.extend({ var Todo = Backbone.Model.extend({    toggle: function() {     toggle: function() {      this.save({done: !this.get("done")});       this.save({done: !this.get("done")});    },     },    clear: function() {     clear: function() {      this.destroy();       this.destroy();    }     }}); });var TodoList = Backbone.Collection.extend({ var TodoList = Backbone.Collection.extend({    model: Todo,     model: Todo,    done: function() {     done: function() {      return this.filter(function(todo){ return todo.get(done); });       return this.filter(function(todo){ return todo.get(done); });    },     },    remaining: function() {     remaining: function() {      return this.without.apply(this, this.done());       return this.without.apply(this, this.done());    }     }}); });
  19. 19. Backbone.Sync● create() POST /collection● read() GET /collection[/id]● update() PUT /collection/id● delete() DELETE /collection/id
  20. 20. Etape 3Stocker localement les données
  21. 21. 3 options
  22. 22. FileSystem API ! seulement
  23. 23. window.requestFileSystem  = window.requestFileSystem  window.requestFileSystem  = window.requestFileSystem        || window.webkitRequestFileSystem;       || window.webkitRequestFileSystem;window.requestFileSystem(window.TEMPORARY,  window.requestFileSystem(window.TEMPORARY,    5*1024*1024,   5*1024*1024,                         onInitFs, errorHandler);                          onInitFs, errorHandler);   window.webkitStorageInfo.requestQuota(window.PERSISTENT,  window.webkitStorageInfo.requestQuota(window.PERSISTENT,                                       5*1024*1024,                                       5*1024*1024,                                      initFS, errorHandler);                                       initFS, errorHandler);function initFS(grantedBytes) { function initFS(grantedBytes) {  window.requestFileSystem(PERSISTENT, grantedBytes,    window.requestFileSystem(PERSISTENT, grantedBytes,         onInitFs, errorHandler);        onInitFs, errorHandler);}}
  24. 24. FileErrorcodeQUOTA_EXCEEDED_ERRNOT_FOUND_ERRSECURITY_ERRINVALID_MODIFICATION_ERRINVALID_STATE_ERR
  25. 25. function onInitFs(fs) { function onInitFs(fs) {  fs.root.getFile(data.txt, {create: true}, function(entry) {   fs.root.getFile(data.txt, {create: true}, function(entry) {    entry.file(function(file) {     entry.file(function(file) {                      var reader = new FileReader();        var reader = new FileReader();       reader.onloadend = function(e) {        reader.onloadend = function(e) {         repository.init(JSON.parse(e.target.result));          repository.init(JSON.parse(e.target.result));       };        };       reader.readAsText(file);        reader.readAsText(file);             }, errorHandler);     }, errorHandler);  }, errorHandler);   }, errorHandler);}}
  26. 26. FileEntrynamefullpathisFileisDirectory…file()createWriter()moveTo()copyTo()remove()...
  27. 27. function persistData(fs, data) { function persistData(fs, data) {  fs.root.getFile(log.txt, {create: true}, function(entry) {   fs.root.getFile(log.txt, {create: true}, function(entry) {    entry.createWriter(function(writer) {     entry.createWriter(function(writer) {      writer.onwriteend = function(e) {       writer.onwriteend = function(e) {        console.log(Write completed.);         console.log(Write completed.);      };       };      writer.onerror = function(e) {       writer.onerror = function(e) {        console.log(Write failed:  + e.toString());         console.log(Write failed:  + e.toString());      };       };      var bb = new BlobBuilder();       var bb = new BlobBuilder();      bb.append(JSON.stringify(data));       bb.append(JSON.stringify(data));      writer.write(bb.getBlob(text/plain));       writer.write(bb.getBlob(text/plain));    }, errorHandler);     }, errorHandler);  }, errorHandler);   }, errorHandler);}}
  28. 28. Web StoragelocalStorage et sessionStorage
  29. 29. Un simple dépôt clé-valeurlocalStorage.setItem("foo", "bar"); localStorage.setItem("foo", "bar");localStorage.setItem("tweets", JSON.stringify(tweets); localStorage.setItem("tweets", JSON.stringify(tweets);var tweets = JSON.parse(localStorage.getItem("tweets")); var tweets = JSON.parse(localStorage.getItem("tweets"));
  30. 30. Quota de 5 Mo! Pas de transactions Pas dindexation
  31. 31. localStorage["tweets:1234"] = "Lorem ipsum..."; localStorage["tweets:1234"] = "Lorem ipsum...";localStorage["tweets:1235"] = "Nam mauris lorem..."; localStorage["tweets:1235"] = "Nam mauris lorem...";localStorage["tags"] = JSON.stringify(["Java", "Python",    localStorage["tags"] = JSON.stringify(["Java", "Python",                                         "Ruby", "PHP"]);                                       "Ruby", "PHP"]);localStorage["tags:Java"] = JSON.stringify([1234, 1235]); localStorage["tags:Java"] = JSON.stringify([1234, 1235]);
  32. 32. ➔ Très bien supporté ➔ Performances➔ API simple ➔ API synchrone➔ Mis en avant par de nb ➔ Sérialisation compagnies/projets ➔ Requêtes ➔ Complexité ➔ "indexation" manuelle ➔ Maintien intégrité
  33. 33. >= 8 >= 3.5 >= 4.0 >= 4.0 >= 10.5 >= 3.2 >= 2.1 >= 11.0
  34. 34. localStorage adapterBackbone.sync = function(method, model, options) { Backbone.sync = function(method, model, options) {  var resp;   var resp;  var store = model.localStorage || model.collection.localStorage;   var store = model.localStorage || model.collection.localStorage;  switch (method) {   switch (method) {    case "read":    resp = model.id ? store.find(model) :      case "read":    resp = model.id ? store.find(model) :    store.findAll();    store.findAll();          break;          break;    case "create":  resp = store.create(model);                                     case "create":  resp = store.create(model);                                         break;          break;    case "update":  resp = store.update(model);                                 case "update":  resp = store.update(model);                                     break;          break;    case "delete":  resp = store.destroy(model);                                case "delete":  resp = store.destroy(model);                                    break;          break;  }   }  if (resp) {   if (resp) {    options.success(resp);     options.success(resp);  } else {   } else {    options.error("Record not found");     options.error("Record not found");  }   }}; };
  35. 35. Petit intermède Vous connaissez Redis ?BankersBoxvar bb = new BankersBox(1); var bb = new BankersBox(1);bb.set("foo", "bar"); bb.set("foo", "bar");bb.get("foo"); // returns "bar" bb.get("foo"); // returns "bar"bb.set("count", 10); bb.set("count", 10);bb.incr("count"); // sets "count" to 11, returns 11 bb.incr("count"); // sets "count" to 11, returns 11bb.incr("newcount"); // sets "newcount" to 1, returns 1 bb.incr("newcount"); // sets "newcount" to 1, returns 1bb.lpush("mylist", "hello"); bb.lpush("mylist", "hello");bb.lrange("mylist", 0, ­1); // returns ["hello"] bb.lrange("mylist", 0, ­1); // returns ["hello"]bb.rpush("mylist", "world"); bb.rpush("mylist", "world");bb.lrange("mylist", 0, ­1); // returns ["hello", "world"] bb.lrange("mylist", 0, ­1); // returns ["hello", "world"]bb.sadd("myset", "apple"); bb.sadd("myset", "apple");bb.sadd("myset", "oragne"); bb.sadd("myset", "oragne");
  36. 36. IndexedDB
  37. 37. Préfixes, préfixes...window.indexedDB = window.indexedDB || window.mozIndexedDB ||      window.indexedDB = window.indexedDB || window.mozIndexedDB ||                        window.webkitIndexedDB;                    window.webkitIndexedDB;window.IDBKeyRange = window.IDBKeyRange ||                         window.IDBKeyRange = window.IDBKeyRange ||                                             window.webkitIDBKeyRange;                      window.webkitIDBKeyRange;window.IDBTransaction = window.IDBTransaction ||                   window.IDBTransaction = window.IDBTransaction ||                                          window.webkitIDBTransaction;                         window.webkitIDBTransaction;
  38. 38. API asynchronefunction IndexedDBAdapter() { function IndexedDBAdapter() { this.db = null; this.db = null; var request = indexedDB.open("contactApp"); var request = indexedDB.open("contactApp"); request.onsuccess = function(e) { request.onsuccess = function(e) { this.db = e.target.result; this.db = e.target.result; }.bind(this); }.bind(this); request.onfailure = function(e) { request.onfailure = function(e) { console.log("Could not connect to the database"); console.log("Could not connect to the database"); }}}; };
  39. 39. Création dun dépôtIndexedDBAdapter.prototype.create = function(storeName) { IndexedDBAdapter.prototype.create = function(storeName) { var v = "1.0"; var v = "1.0"; var request = this.db.setVersion(v); var request = this.db.setVersion(v); request.onsuccess = function(e) { request.onsuccess = function(e) { var store = this.db.createObjectStore(storeName, { var store = this.db.createObjectStore(storeName, { "keyPath": "id", "keyPath": "id", "autoIncrement": true "autoIncrement": true }); }); }; }; request.onblocked = function(e) { request.onblocked = function(e) { console.log("The database is open in another tab."); console.log("The database is open in another tab."); }; };}; };
  40. 40. Persistence dun objetIndexedDBAdapter.prototype.save(storeName, object, callback)  IndexedDBAdapter.prototype.save(storeName, object, callback) {{ var trans = db.transaction([storeName],                    var trans = db.transaction([storeName],                                                 IDBTransaction.READ_WRITE, 0);                               IDBTransaction.READ_WRITE, 0); var store = trans.objectStore(storeName); var store = trans.objectStore(storeName); var request = store.put(object); var request = store.put(object); request.onsuccess = function(e) { request.onsuccess = function(e) { callback(object); callback(object); }; };}; };
  41. 41. Requête simpleIndexedDBAdapter.prototype.all(storeName, callback) { IndexedDBAdapter.prototype.all(storeName, callback) { var trans = db.transaction([storeName],                    var trans = db.transaction([storeName],                                                 IDBTransaction.READ_WRITE, 0);                               IDBTransaction.READ_WRITE, 0); var store = trans.objectStore(storeName); var store = trans.objectStore(storeName); var keyRange = IDBKeyRange.lowerBound(0); var keyRange = IDBKeyRange.lowerBound(0); var request = store.openCursor(keyRange); var request = store.openCursor(keyRange); request.onsuccess = function(e) { request.onsuccess = function(e) { var cursor = e.target.result; var cursor = e.target.result; if (cursor) { if (cursor) { callback(cursor.value); callback(cursor.value); cursor.continue(); cursor.continue(); }} }; };}; };
  42. 42. Key RangesIDBKeyRange.upperBound(x);            // <= x IDBKeyRange.upperBound(x);            // <= xIDBKeyRange.upperBound(x, true);      // < x IDBKeyRange.upperBound(x, true);      // < xIDBKeyRange.lowerBound(y);            // >= y IDBKeyRange.lowerBound(y);            // >= yIDBKeyRange.lowerBound(y, true);      // > y IDBKeyRange.lowerBound(y, true);      // > yIDBKeyRange.bound(x, y);              // >= x && <= y IDBKeyRange.bound(x, y);              // >= x && <= yIDBKeyRange.bound(x, y, true, false); // > x && <= y IDBKeyRange.bound(x, y, true, false); // > x && <= y
  43. 43. Indexvar contacts = [ var contacts = [ { firstname: "John", lastname: "Doe", email: "jdoe@zz.com" }, { firstname: "John", lastname: "Doe", email: "jdoe@zz.com" }, { firstname: "Jane", lastname: "Doe", email: "jane@zz.com" }, { firstname: "Jane", lastname: "Doe", email: "jane@zz.com" }, { firstname: "Johnny", lastname: "Carson", email: "jca@zz.com" } { firstname: "Johnny", lastname: "Carson", email: "jca@zz.com" }]; ];objectStore.createIndex("firstname", "firstname", { unique: false }); objectStore.createIndex("firstname", "firstname", { unique: false });objectStore.createIndex("lastname", "lastname", { unique: false }); objectStore.createIndex("lastname", "lastname", { unique: false });objectStore.createIndex("email", "email", { unique: true }); objectStore.createIndex("email", "email", { unique: true });
  44. 44. Indexvar contacts = [ var contacts = [ { firstname: "John", lastname: "Doe", email: "jdoe@zz.com" }, { firstname: "John", lastname: "Doe", email: "jdoe@zz.com" }, { firstname: "Jane", lastname: "Doe", email: "jane@zz.com" }, { firstname: "Jane", lastname: "Doe", email: "jane@zz.com" }, { firstname: "Johnny", lastname: "Carson", email: "jca@zz.com" } { firstname: "Johnny", lastname: "Carson", email: "jca@zz.com" }]; ];var index = objectStore.index("lastname"); var index = objectStore.index("lastname");index.openCursor(IDBKeyRange.only("Doe")).onsuccess = function(e) { index.openCursor(IDBKeyRange.only("Doe")).onsuccess = function(e) { var cursor = e.target.result; var cursor = e.target.result; if (cursor) { if (cursor) { callback(cursor.value); callback(cursor.value); cursor.continue(); cursor.continue(); }}}; };var index = objectStore.index("firstname"); var index = objectStore.index("firstname");index.openCursor(IDBKeyRange.lowerbound("John")).onsuccess =  index.openCursor(IDBKeyRange.lowerbound("John")).onsuccess = function(e) { function(e) { ... ...}; };
  45. 45. >= 10 ? >= 8.0 >= 16.0 ? ? ? ? ?
  46. 46. Et pourquoi pasWeb SQL Database ?
  47. 47. X X >= 4.0 >= 3.1 >= 10.5 >= 3.2 >= 2.1 >= 11.0
  48. 48. Le polyfill ultime ? Lawnchair ● localStorage ● indexedDB ● webkit­sqlite ● gears­sqlite ● ie­userdata ● backberry­persistent­store ● window­name
  49. 49. Etape 4Développer une stratégie de synchronisation
  50. 50. Lexemple dActiveSync
  51. 51. POST /Microsoft­Server­ActiveSync?User=jdoe@zz.com&...POST /Microsoft­Server­ActiveSync?User=jdoe@zz.com&...     &Cmd=<Commande>     &Cmd=<Commande>Cmd=FolderSync&SyncKey=123456789Cmd=FolderSync&SyncKey=123456789{{ folders: [ folders: [ contacts: { contacts: { id: 1234, id: 1234, created: 0, created: 0, updated: 2, updated: 2, deleted: 1 deleted: 1 }, }, todos: { todos: { id: 5678, id: 5678, created: 5, created: 5, updated: 0, updated: 0, deleted: 2 deleted: 2 } } ] ]}}
  52. 52. Cmd=Sync&SyncKey=123456789&FolderId=5678Cmd=Sync&SyncKey=123456789&FolderId=5678{{ syncKey: 987654321, syncKey: 987654321,   created: [   created: [ { id: 4321, label: "Acheter le pain" }, { id: 4321, label: "Acheter le pain" }, ... ... ], ], updated: [ updated: [ ... ... ], ], deleted: [ deleted: [ 5678, 7890 5678, 7890 ] ]}}
  53. 53. Autres pistesMozilla ServicesSyncMLOperational Transformation → ShareJS
  54. 54. Etape subsidiaire Le "push"
  55. 55. PollingClient Serveur n secondes n secondes
  56. 56. Long polling (COMET) Client Serveur Événement côté serveur Événement côté serveur
  57. 57. WebSockets
  58. 58. HandshakeGET /resource name/ HTTP/1.1Upgrade: WebSocketConnection: UpgradeHost: /server/Origin: /origin/WebSocket­Protocol: /protocol/HTTP/1.1 101 Web Socket Protocol HandshakeUpgrade: WebSocketConnection: UpgradeWebSocket­Origin: /origin/WebSocket­Location: /url/WebSocket­Protocol: /subprotocol/
  59. 59. Serveur (avec node.js) var io = require(socket.io).listen(80); var io = require(socket.io).listen(80); io.sockets.on(connection, function (socket) { io.sockets.on(connection, function (socket) { socket.emit(news, { hello: world }); socket.emit(news, { hello: world }); socket.on(my other event, function (data) { socket.on(my other event, function (data) { console.log(data); console.log(data); }); }); }); });Client var socket = io.connect(http://localhost); var socket = io.connect(http://localhost); socket.on(news, function (data) { socket.on(news, function (data) { console.log(data); console.log(data); socket.emit(my other event, { my: data }); socket.emit(my other event, { my: data }); }); });
  60. 60. >= 10 ? >= 6.0 >= 14.0 >= 5.0 >= 11.0 >= 4.2 ? >= 11.0
  61. 61. Avec Socket.IO>= 5.5 >= 3.0 >= 4.0 >= 3.0 >= 10.61 >= 3.2 >= 2.1 >= 11.0
  62. 62. Server-Sent Events
  63. 63. HTTP traditionnelGET /myAppStreamContent­Type: text/event­stream
  64. 64. if (window.EventSource) {if (window.EventSource) {  var source = new EventSource(myAppStream);  var source = new EventSource(myAppStream);} else {} else {  // fallback to something...  // fallback to something...}}source.addEventListener(message, function(e) {source.addEventListener(message, function(e) {  console.log(e.data);  console.log(e.data);}, false);}, false);source.addEventListener(open, function(e) {source.addEventListener(open, function(e) {    }, false);}, false);source.addEventListener(error, function(e) {source.addEventListener(error, function(e) {  if (e.readyState == EventSource.CLOSED) {  if (e.readyState == EventSource.CLOSED) {          }  }}, false);}, false);
  65. 65. Format des messagesdata: Hello worldnnMulti-lignedata: Hellondata: worldnnJSONdata: {ndata: "msg": "Hello world",ndata: "id": 12345ndata: }nn
  66. 66. Format des messagesUtilisation des IDsid: 12345ndata: {"user": "goldo", "msg": "Hello !"}nnUtilisation des eventsdata: {"msg": "Hello !"}nnevent: loginndata: {"user": "goldo"}nnevent: updatendata: {"user": "goldo", "status": "away"}nn
  67. 67. AvantagesReconnexion automatiqueIdsEvents
  68. 68. Questions / réponses

×