html5-img
1 / 47

Designing Real-Time Applications

Designing Real-Time Applications. Modular, Robust, Secure, and Scalable Flash Communication Applications. Modular and Robust Applications. Cohesive loosely coupled components Module: Flash Component + Server-Side Object Cohesive:

nishi
Télécharger la présentation

Designing Real-Time Applications

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Designing Real-Time Applications Modular, Robust, Secure, and Scalable Flash Communication Applications

  2. Modular and Robust Applications • Cohesive loosely coupled components • Module: Flash Component + Server-Side Object • Cohesive: • Completely manage all streams, shared objects, and remote methods needed by the component. • Inlcudes: model, view, and controller. • Reusable in many different applications. • Loosely Coupled: • Can exist without most other components • Can make available public read only model to other components (Status and PeopleList)

  3. Credit Where it is Due! • Use Macromedia’s v2 components • Heavily influenced by Macromedia’s Communication Components • Different set of assumptions: • Always authenticate users. • Started as simply as possible – no framework. • Eventually built rudimentary framework to simplify code, provide common services, and provide security. • Do not need automagic creation of server-side objects.

  4. Component-Based Sample Application • People List – list of connected users • Status – allow each user to indicate busy, away, offline, online • Shared Text – common meeting notes • Text Chat • Video Conference

  5. Shared Text Component in Depth • Events and data management • Server-side SharedText Class • Client-side SharedText v2 based Component

  6. blesser: peldi:

  7. blesser: peldi:

  8. blesser: peldi: Note: All slot names are strings. Even slot names like 1 are string values.

  9. blesser: peldi: Note: All slot names are strings. Even slot names like 1 are string values.

  10. blesser: peldi: Note: All slot names are strings. Even slot names like 1 are string values.

  11. blesser: peldi: Note: All slot names are strings. Even slot names like 1 are string values.

  12. blesser: peldi: Note: All slot names are strings. Even slot names like 1 are string values.

  13. Client Client Server SharedText messages SharedText messages SharedText messages Component Shared Object Component Shared Object Component Shared Object client nc.call("edit"); edit(userName) setProperty("curentUser", userName) data copied to client's copy of the shared object onSync(list) data copied to client's copy of the shared object onSync(ev) onSync(list) onSync(ev)

  14. Server-Side SharedText Class • Multiple SharedText objects can be created. • Each has its own namespace. • pfcs/SharedText/main • pfcs/SharedText/breakout1 • pfcs/SharedText/breakout2 • Format: • pfcs/ComponentClassName/name

  15. The main.asc file - 1 load("TextChat.asc"); load("SharedText.asc"); load("Status.asc"); load("PeopleList.asc"); application.onAppStart = function(){ userList_so = SharedObject.get("private/userList"); sharedText = new SharedText("main"); textChat = new TextChat("main"); status = new Status("main"); peopleList = new PeopleList("main"); }

  16. The main.asc file - 2 application.onConnect = function(client, user){ // authentication code removed... client.pfcs = {user:user}; client.pfcs.user.arrival = new Date(); client.readAccess = "pfcs/"; client.writeAccess = "pfcs/"; peopleList.addClient(client); status.addClient(client); textChat.addClient(client); sharedText.addClient(client); userList_so.setProperty(client.pfcs.user.userName, client); return true; } application.onDisconnect = function(client){ sharedText.removeClient(client); textChat.removeClient(client); status.removeClient(client); peopleList.removeClient(client); userList_so.setProperty(client.pfcs.user.userName, null); }

  17. /** * SharedText constructor is passed it's name. The default is main. * All component resources exist in the namespace: * pfcs/SharedText/name/ * Gets and initializes the pfcs/SharedText/name/messages so. */ function SharedText(name){ this.name = name; if (!name) this.name = "main"; this.path = "pfcs/" + this.className + "/" + name; this.so = SharedObject.get(this.path + "/messages", true); this.so.ready = false; this.so.onSync = function(){ var currentUser = this.getProperty("currentUser"); if (currentUser != "") this.setProperty("currentUser", ""); } this.so.setProperty("currentUser", ""); if (typeof this.so.getProperty("length") != "number") { this.so.setProperty("length", 0); } } SharedText.prototype.className = "SharedText";

  18. /** * Adds an object path to a client object. For example if the * path is "pfcs/SharedText/main" then the client will * have the following objects added if they do not already * exist: * client.pfcs * client.pfcs.SharedText * client.pfcs.SharedText.main */ SharedText.prototype.addObjectPath = function(client){ var pathArray = this.path.split("/"); var obj = client; var name; for (var i = 0; i < pathArray.length; i++){ name = pathArray[i]; if (! obj[name]){ obj[name] = {}; } obj = obj[name]; } }

  19. /** addClient(client) is called whenever a client needs to connect * to the shared text component. If there is only one shared text * component this is likely called when each client connects * to an instance. */ SharedText.prototype.addClient = function(client){ this.addObjectPath(client); var thisComponent = this; client.pfcs[this.className][this.name].edit = function(){ thisComponent.edit(client.pfcs.user.userName); } } /** removeClient(client) is called whenever a client no longer * needs the shared text component. If there is only one shared * text component this is likely called when each client * disconnects from an instance. */ SharedText.prototype.removeClient = function(client){ var currentUser = this.so.getProperty("currentUser"); if (client.pfcs.user.userName == currentUser) { this.so.setProperty("currentUser", ""); } delete client.pfcs[this.className][this.name]; }

  20. /** * edit(userName) is called by a client to get or release control * of the shared text component. The messages shared object * currentUser slot may have one of three values. It can be * empty, contain someone else's user name or contain * the userName of the person calling this method. */ SharedText.prototype.edit = function(userName){ var currentUser = this.so.getProperty("currentUser"); if (userName == currentUser) this.so.setProperty("currentUser", ""); else if (currentUser != "") return; else this.so.setProperty("currentUser", userName); }

  21. Client-Side Shared Text Component

  22. The SharedText.as file import mx.controls.NumericStepper; import mx.controls.Button; import mx.controls.TextArea; import mx.controls.Label; class SharedText extends mx.core.UIComponent { var className:String = "SharedText"; static var symbolName:String = "SharedText"; static var symbolOwner:Object = SharedText; //… }

  23. function createChildren() { var depth = 100; var instance = this; createObject("TextArea", "sharedTextArea", depth++); createObject("NumericStepper", "versionStepper", depth++); createObject("Button", "editButton", depth++); createObject("Button", "sendButton", depth++); createObject("Button", "selectButton", depth++); createObject("Button", "deleteButton", depth++); createObject("Label", "versionLabel", depth++); versionLabel.html = true; versionLabel.text = "<B>Version:</B>"; //… editButton.addEventListener("click", this); sendButton.addEventListener("click", this); selectButton.addEventListener("click", this); deleteButton.addEventListener("click", this); versionStepper.addEventListener("change", this); }

  24. var path:String = "pfcs/SharedText/main/"; function edit(ev) { __nc.call(path + "edit", null) if (ev.target.label == "Cancel") { replaceSharedText(); } } function click(ev) { switch (ev.target) { case editButton : edit(ev); break; case sendButton : shareText(ev); break; case selectButton : selectText(ev); break; case deleteButton : deleteVersion(ev); break; } }

  25. function onSync(ev) { if (ev.list.length == 0 && messages.data.length > 0){ replaceSharedText(); updateButtons(); return; } var info; for (var i in ev.list) { info = ev.list[i]; if (info.name == "length") { replaceSharedText(); } else if (info.name == "currentUser") { updateButtons(); } else if (info.name == "selectCount") { showSelection(); } } }

  26. function replaceSharedText(ev) { var len = messages.data.length; if (len == 0) { sharedTextArea.text = ""; versionStepper.minimum = 0; versionStepper.maximum = 0; versionStepper.value = 0; return; } sharedTextArea.text = messages.data[len]; versionStepper.minimum = 1; versionStepper.maximum = len; versionStepper.value = len; }

  27. function updateButtons() { var currentUser = messages.data.currentUser; if (currentUser == "" && __user) { editButton.enabled = true; editButton.selected = false; editButton.label = "Edit"; sendButton.enabled = false; sendButton.selected = false; selectButton.enabled = false; selectButton.selected = false; deleteButton.enabled = false; deleteButton.selected = false; sharedTextArea.editable = false; } else if (currentUser == __user.userName && __user) { editButton.enabled = true; //… } else { editButton.selected = false; //… } }

  28. function shareText(ev) { var len = messages.data.length; if (messages.data[len] == sharedTextArea.text) { return; } len++; messages.setFPS(0); messages.data.length = len; messages.data[len] = sharedTextArea.text; messages.setFPS(12); }

  29. Communication “Components” • Problem: components modularize applications but can act as isolated “mini applications.” • Solution: component interactions: • Provide model and/or view to other components • Provide controller on both client and server • Broadcast events • Examples: • PeopleList and Status Component • PeopleGrid and Status, Pace, and Question Components

  30. PeopleList/Status Example • PeopleList component maintains its own peopleList shared object mapping user names to user information. • PeopleList can be used without the Status component. • Status component maintains a shared object that maps user names to user connection status. • Status component exposes the statusList shared object to the PeopleList.

  31. Client Server PeopleList Status statusList Status statusList Component Component Shared Object Component Shared Object addClient(client) setProperty(userName,...) onSync(list) setStatus(status) setProperty(userName,...) onSync(list) removeClient() setProperty(userName,...) onSync(list)

  32. function clone(orig) { var copy = {}; for (var p in orig) { copy[p] = orig[p]; } return copy; } function onSync(ev){ // Get the list's dataProvider and truncate it. var dp = list.dataProvider; dp.length = 0; // Fill the dataProvider using an Array method. for (var p in __peopleList.data) { var obj = clone(__peopleList.data[p]); var status = __statusList.data[p]; if (status) { obj.status = status; // Skip this record to make a person disappear from the list if (__hideOfflineUsers && obj.status == "Offline") continue; } dp.push({label:p, data:obj}); } dp.dispatchEvent({target:dp, type:"modelChanged"}); }

  33. Designing Secure Applications • Authentication Components and Subclasses were not available in Macromedia’s component set • Macromedia’s components used a root level namespace making their resources impossible to protect without altering code • No provision for role-based access controls

  34. Server-side Authenticationapplication methods: • onConnect (client, credentials); • Handle different types of clients: SWF and FCS instances • Handle different credentials: guests on a local database and enterprise users on a directory service • Standard method provided in FCS • onAuthenticate (client, credentials); • Once you know who someone is you may want to use different methods to authorize their connection • Non-standard application method • onAuthorize (client, credentials); • Additional tests and post connection work • Non-standard application method

  35. // Load the component manager and base class. load("ComponentManager.asc"); load("Component.asc"); // Load the authentication and authorization object. load("Auth.asc"); // Create Auth object and components auth = new Auth("http://myhost.xxx/flashservices/gateway", "authDir.services.authService"); cManager = new ComponentManager(); application.onConnect = function client(client, credentials){ return auth.authenticate(client, credentials); } application.onAuthenticate = function(client, credentials){ auth.authorize(client, credentials); } application.onAuthorize = function(client, credentials){ this.acceptConnection(client); cManager.addClient(client); }

  36. Role-based Access • Each component has its own namespace: • pfcs/SharedText/main • pfcs/SharedText/breakout1 • pfcs/SharedText/breakout2 • Client read and write access strings: client.readAccess = “pfcs/SharedText/main; pfcs/TextChat/main/messages;pfcs/PeopleList/main” client.writeAccess = “pfcs/SharedText/main”

  37. function Component(name){ } Component.prototype.className = "Component"; Component.prototype.addClient = function(client){ // Reject banned clients // Get list of matching user roles and extract permissions // Let subclass take it from there } Component.prototype.removeClient = function(client){ // Remove access to this component's resources from client // Let subclass do more... } Component.prototype.addBannedUsers = function(userHash){} Component.prototype.clientInRoles = function(client){} Component.prototype.addClientPath = function(path, permissions){} Component.prototype.removeClientPath = function(path){}

  38. Scalable Applications • Single-server multi-instance applications • Multi-server lobby and rooms • Instance trees and components • Using Application Servers to bridge applications and instances

  39. Best Case: One way “Broadcast” Stream 12 Streams from server to clients. 3 Streams from root server to leaf servers. 4 Streams from leaf server to SWFs.

  40. Worst Case: Video Conference 12 Clients, each client sends one stream and receives 11. Server handles 144 Streams (12 + 11 * 12). Each leaf sends 4 streams to the root and receives 8. Root server handles 36 streams. Each leaf server handles 12 streams to/from the root, 4 incoming client streams, and 44 streams to clients. Total: 60.

  41. Three Part Components • Flash UI Component • Connects to leaf server • Server-side leaf object • Connects to root server and establishes any required proxies • Adds/removes clients • Server-side root object • Adds/removes only leaf object clients

  42. Flash ApplicationServer Database

  43. Flash FCS Application Server Database

  44. References • flash-communications.net • flash-communications.net/technotes/fitc2004/ • echo.ryerson.ca Not done yet!

More Related