📄 mmpdev-article.xhtml
字号:
<span class="py-src-comment"># Remote methods.</span> <span class="py-src-keyword">def</span><a name="perspective_goToPsychologist"><span class="py-src-identifier"> perspective_goToPsychologist</span></a>(self, name): psychologist = self.service.psychologists.get(name) <span class="py-src-keyword">if</span> psychologist: psychologist.joinTherapy(self) self.psychologist = psychologist <span class="py-src-keyword">return</span> <span class="py-src-string">"You made it to group therapy."</span> <span class="py-src-keyword">else</span>: <span class="py-src-keyword">return</span> <span class="py-src-string">"There's no such psychologist."</span> <span class="py-src-keyword">def</span><a name="perspective_psychoanalyze"><span class="py-src-identifier"> perspective_psychoanalyze</span></a>(self): <span class="py-src-keyword">if</span> self.psychologist: self.psychologist.requestTherapy(self) <span class="py-src-keyword">return</span> <span class="py-src-string">"Therapy requested!"</span> <span class="py-src-keyword">else</span>: <span class="py-src-keyword">return</span> <span class="py-src-string">"You're not near a therapist."</span> <span class="py-src-comment"># Local methods.</span> <span class="py-src-keyword">def</span><a name="heal"><span class="py-src-identifier"> heal</span></a>(self, points): self.angst -= points <span class="py-src-keyword">if</span> points > 0: feeling = <span class="py-src-string">"better"</span> <span class="py-src-keyword">else</span>: feeling = <span class="py-src-string">"worse"</span> self.remoteBugWatcher.callRemote( <span class="py-src-string">"healed"</span>, <span class="py-src-string">"You feel %s points %s. Your angst is now: %s."</span> % (abs(points), feeling, self.angst))<span class="py-src-keyword">class</span><a name="Psychologist"><span class="py-src-identifier"> Psychologist</span></a>: <span class="py-src-keyword">def</span><a name="__init__"><span class="py-src-identifier"> __init__</span></a>(self, name, skill, world): self.name = name self.skill = skill self.group = [] world.psychologists[name] = self <span class="py-src-keyword">def</span><a name="joinTherapy"><span class="py-src-identifier"> joinTherapy</span></a>(self, bug): self.group.append(bug) <span class="py-src-keyword">def</span><a name="leaveTherapy"><span class="py-src-identifier"> leaveTherapy</span></a>(self, bug): self.group.remove(bug) <span class="py-src-keyword">def</span><a name="requestTherapy"><span class="py-src-identifier"> requestTherapy</span></a>(self, bug): <span class="py-src-keyword">for</span> bug <span class="py-src-keyword">in</span> self.group: bug.heal(self.skill + len(self.group))<span class="py-src-keyword">class</span><a name="BuggyWorld"><span class="py-src-identifier"> BuggyWorld</span></a>(Service): perspectiveClass = Bug <span class="py-src-keyword">def</span><a name="__init__"><span class="py-src-identifier"> __init__</span></a>(self, *args, **kw): Service.__init__(self, *args, **kw) self.psychologists = {} Psychologist(<span class="py-src-string">"Freud"</span>, -5, self) Psychologist(<span class="py-src-string">"Pavlov"</span>, 1, self) Psychologist(<span class="py-src-string">"The Tick"</span>, 10, self)</pre><div class="py-caption">Metamorphosis Game module - <span class="py-filename">metamorph.py</span></div></div><p>In this second listing, we define an extremely simple game. This providesan additional class for the simulation beyond the Perspective and Service (thePsychologist), to demonstrate mediated communication between perspectives.Again, <code>perspective_</code> methods comprise the remote interfacebetween clients and servers for this game.</p><p>We have now implemented two modules which are complete, but completelyunrelated. The way in which they've been implemented is important, however.Their parallel structure, imposed by Perspective Broker, allows us to integratethem into a single server with just one short file. </p><div class="py-listing"><pre class="python"><span class="py-src-comment"># Import Twisted code</span><span class="py-src-keyword">from</span> twisted.spread.pb <span class="py-src-keyword">import</span> AuthRoot, BrokerFactory, portno<span class="py-src-keyword">from</span> twisted.internet.app <span class="py-src-keyword">import</span> Application<span class="py-src-keyword">from</span> twisted.cred.authorizer <span class="py-src-keyword">import</span> DefaultAuthorizer<span class="py-src-comment"># Import our own library</span><span class="py-src-keyword">import</span> cellphone<span class="py-src-keyword">import</span> metamorph<span class="py-src-comment"># Create root-level object and authorizer</span>app = Application(<span class="py-src-string">"Metamorphosis"</span>)auth = DefaultAuthorizer(app)<span class="py-src-comment"># Create our services (inside the App directory)</span>phoneCompany = cellphone.PhoneCompany(<span class="py-src-string">"cellphone"</span>, app, auth)buggyWorld = metamorph.BuggyWorld(<span class="py-src-string">"metamorph"</span>, app, auth)<span class="py-src-comment"># Create Identities for Joe and Bob.</span><span class="py-src-keyword">def</span><a name="makeAccount"><span class="py-src-identifier"> makeAccount</span></a>(userName, phoneNumber, bugName, pw): <span class="py-src-comment"># Create a cell phone for the player, so they can chat.</span> phone = phoneCompany.createPerspective(phoneNumber) <span class="py-src-comment"># Create a bug for the player, so they can play the game.</span> bug = buggyWorld.createPerspective(bugName) <span class="py-src-comment"># Create an identity for the player, so they can log in.</span> i = auth.createIdentity(userName) i.setPassword(pw) <span class="py-src-comment"># add perspectives to identity we created</span> i.addKeyForPerspective(phone) i.addKeyForPerspective(bug) <span class="py-src-comment"># Finally, commit the identity back to its authorizer.</span> i.save()<span class="py-src-comment"># Create both Bob's and Joe's accounts.</span>makeAccount(<span class="py-src-string">"joe"</span>, <span class="py-src-string">"222-303-8484"</span>, <span class="py-src-string">"fritz"</span>, <span class="py-src-string">"joepass"</span>)makeAccount(<span class="py-src-string">"bob"</span>, <span class="py-src-string">"222-303-8485"</span>, <span class="py-src-string">"franz"</span>, <span class="py-src-string">"bobpass"</span>)app.listenTCP(portno, BrokerFactory(AuthRoot(auth)))app.run()</pre><div class="py-caption">Account Manipulation Example - <span class="py-filename">fritz-franz-setup.py</span></div></div><p>Finally, we unify these services by creating accounts for Bob and Joe. Thereal meat of this example is the makeAccount method, which produces Identityinstances that have the correct Perspectives attached.</p><p>By modifying this one file, we can add arbitrary additional services. Thereare three steps to this process:<ol> <li>Instantiate the new <code>Service</code> (in the section "Create our services...")</li> <li>Call our service's <code>createPerspective</code> method to generate a new perspective.</li> <li>Add a key for that perspective to the appropriate <code>Identity</code>object, declaring that a user who presents credentials to that<code>Identity</code> is permitted access to it.</li></ol></p><p>Now, how do Joe and Bob actually get into the game? A full-blown,interactive client could be the subject of another article, but here is asimple example that will log into <u>Metamorphosis</u> and play it for 5seconds.</p><div class="py-listing"><pre class="python"><span class="py-src-keyword">from</span> twisted.spread.pb <span class="py-src-keyword">import</span> authIdentity, getObjectAt, portno<span class="py-src-keyword">from</span> twisted.spread.flavors <span class="py-src-keyword">import</span> Referenceable<span class="py-src-keyword">from</span> twisted.internet <span class="py-src-keyword">import</span> reactor<span class="py-src-keyword">from</span> twisted.internet.defer <span class="py-src-keyword">import</span> DeferredList<span class="py-src-keyword">class</span><a name="BugClient"><span class="py-src-identifier"> BugClient</span></a>(Referenceable): <span class="py-src-keyword">def</span><a name="gotPerspective"><span class="py-src-identifier"> gotPerspective</span></a>(self, pref): <span class="py-src-keyword">print</span> <span class="py-src-string">'got bug'</span> pref.callRemote(<span class="py-src-string">"goToPsychologist"</span>,<span class="py-src-string">"The Tick"</span>).addCallback(self.notify) pref.callRemote(<span class="py-src-string">"psychoanalyze"</span>).addCallback(self.notify) <span class="py-src-keyword">def</span><a name="notify"><span class="py-src-identifier"> notify</span></a>(self, text): <span class="py-src-keyword">print</span> <span class="py-src-string">'bug:'</span>, text <span class="py-src-keyword">def</span><a name="remote_healed"><span class="py-src-identifier"> remote_healed</span></a>(self, healedMessage): <span class="py-src-keyword">print</span> <span class="py-src-string">'Healed:'</span>, healedMessage<span class="py-src-keyword">class</span><a name="CellClient"><span class="py-src-identifier"> CellClient</span></a>(Referenceable): <span class="py-src-keyword">def</span><a name="gotPerspective"><span class="py-src-identifier"> gotPerspective</span></a>(self, pref): <span class="py-src-keyword">print</span> <span class="py-src-string">'got cell'</span> <span class="py-src-comment"># pref.callRemote()</span> <span class="py-src-keyword">def</span><a name="remote_connected"><span class="py-src-identifier"> remote_connected</span></a>(self): <span class="py-src-keyword">print</span> <span class="py-src-string">'Cell Connected'</span> <span class="py-src-keyword">def</span><a name="remote_hear"><span class="py-src-identifier"> remote_hear</span></a>(self, text): <span class="py-src-keyword">print</span> <span class="py-src-string">'Phone:'</span>, text <span class="py-src-keyword">def</span><a name="remote_ring"><span class="py-src-identifier"> remote_ring</span></a>(self, callerID): <span class="py-src-keyword">print</span> <span class="py-src-string">"Your phone is ringing. It's a call from"</span>, callerIDbugClient = BugClient()phoneClient = CellClient()<span class="py-src-comment"># Log-In Information</span>username = <span class="py-src-string">"bob"</span>password = <span class="py-src-string">"bobpass"</span>bugName = <span class="py-src-string">"franz"</span>phoneNumber = <span class="py-src-string">"222-303-8485"</span> <span class="py-src-comment"># A little magic to get us connected...</span>getObjectAt(<span class="py-src-string">"localhost"</span>, portno).addCallback( <span class="py-src-comment"># challenge-response authentication</span> <span class="py-src-keyword">lambda</span> r: authIdentity(r, username, password)).addCallback( <span class="py-src-comment"># connecting to each perspective with 'attach' method of remote identity</span> <span class="py-src-keyword">lambda</span> i: DeferredList([ i.callRemote(<span class="py-src-string">"attach"</span>, <span class="py-src-string">"metamorph"</span>, bugName, bugClient), i.callRemote(<span class="py-src-string">"attach"</span>, <span class="py-src-string">"cellphone"</span>, phoneNumber, phoneClient)]) <span class="py-src-comment"># connecting perspectives to client-side objects</span> ).addCallback(<span class="py-src-keyword">lambda</span> l: (bugClient.gotPerspective(l[0][1]), phoneClient.gotPerspective(l[1][1])))reactor.callLater(5, reactor.stop) <span class="py-src-comment"># In five seconds, log out.</span>reactor.run() <span class="py-src-comment"># Start the main loop.</span></pre><div class="py-caption">Client Code Example - <span class="py-filename">metaclient.py</span></div></div><p>The primary responsibility of client code is to provide client objects foreach Perspective that exists on the server, to receive notifications that theserver may send. A few lines of glue code are required to get thoseclient-side instances hooked up to server-side perspectives, but these aremostly using glue code provided by the server.</p><div align="center"><img src="login-sequence.png"/> <br/>Figure 1 - Login Sequence</div><p>Figure 1 is a visual explanation of the login sequence that PB applicationsrequire. There are 8 basic steps to the interaction that client and server gothrough in order to get a working session set up, represented by the numberedarrows.</p><ol> <li>The client contacts the authorizer and requests to log in.</li> <li>The client provides a username and password through a challenge/response conversation, ensuring that the password is secure even on un-encrypted connections.</li> <li>The challenge/response process locates an Identity object and validates against it.</li>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -