2010-12-08 1 views
1

こんにちは私はねじれjsonrpcサーバーにrpc呼び出しを行ういくつかのマイクロコントローラを提供するためにねじれに基づいてrpcサーバーを開発に取り組んでいます。しかし、アプリケーションはまた、サーバがいつでも各マイクロに情報を送信することを要求したので、マイクロからの遠隔のjsonrpc呼び出しからの応答が、サーバーのjsonrpc要求と混同されるのを防ぐための良い方法ユーザー。どのように2つの方法jsonrpc +ツイストサーバー/クライアントを実装する

ソケットから来ているnetstring/json文字列が以前の要件からの応答か、サーバーからの新しい要求かどうかわからないので、マイクロは悪い情報を受け取っているという結果になります。あなたは、受信者がそれをどのように解釈するかを決定できるように、各メッセージに十分な情報を追加する必要が

from twisted.internet import reactor 
from txjsonrpc.netstring import jsonrpc 
import weakref 

creds = {'user1':'pass1','user2':'pass2','user3':'pass3'} 

class arduinoRPC(jsonrpc.JSONRPC): 
    def connectionMade(self): 
     pass 

    def jsonrpc_identify(self,username,password,mac): 
     """ Each client must be authenticated just after to be connected calling this rpc """ 
     if creds.has_key(username): 
      if creds[username] == password: 
       authenticated = True 
      else: 
       authenticated = False 
     else: 
      authenticated = False 

     if authenticated: 
      self.factory.clients.append(self) 
      self.factory.references[mac] = weakref.ref(self) 
      return {'results':'Authenticated as %s'%username,'error':None} 
     else: 
      self.transport.loseConnection() 

    def jsonrpc_sync_acq(self,data,f): 
     """Save into django table data acquired from sensors and send ack to gateway""" 
     if not (self in self.factory.clients): 
      self.transport.loseConnection() 
     print f 
     return {'results':'synced %s records'%len(data),'error':'null'} 

    def connectionLost(self, reason): 
     """ mac address is searched and all reference to self.factory.clientes are erased """ 
     for mac in self.factory.references.keys(): 
      if self.factory.references[mac]() == self: 
       print 'Connection closed - Mac address: %s'%mac 
       del self.factory.references[mac] 
       self.factory.clients.remove(self) 


class rpcfactory(jsonrpc.RPCFactory): 
    protocol = arduinoRPC 
    def __init__(self, maxLength=1024): 
     self.maxLength = maxLength 
     self.subHandlers = {} 
     self.clients = [] 
     self.references = {} 

""" Asynchronous remote calling to micros, simulating random calling from server """ 
import threading,time,random,netstring,json 
class asyncGatewayCalls(threading.Thread): 
    def __init__(self,rpcfactory): 
     threading.Thread.__init__(self) 
     self.rpcfactory = rpcfactory 
     """identifiers of each micro/client connected""" 
     self.remoteMacList = ['12:23:23:23:23:23:23','167:67:67:67:67:67:67','90:90:90:90:90:90:90'] 
    def run(self): 
     while True: 
      time.sleep(10) 
      while True: 
       """ call to any of three potential micros connected """ 
       mac = self.remoteMacList[random.randrange(0,len(self.remoteMacList))] 
       if self.rpcfactory.references.has_key(mac): 
        print 'Calling %s'%mac 
        proto = self.rpcfactory.references[mac]() 
        """ requesting echo from selected micro""" 
        dataToSend = netstring.encode(json.dumps({'method':'echo_from_micro','params':['plop']})) 
        proto.transport.write(dataToSend) 
        break 

factory = rpcfactory(arduinoRPC) 

"""start thread caller""" 
r=asyncGatewayCalls(factory) 
r.start() 

reactor.listenTCP(7080, factory) 
print "Micros remote RPC server started" 
reactor.run() 

答えて

2

は、ここに私のコードです。要件はAMPのものとよく似ているので、代わりにAMPを使用するか、AMPと同じ構造を使用してメッセージを識別することができます。具体的には:

  • リクエストでは、特定のキーを入力します。たとえば、AMPは「_ask」を使用してリクエストを識別します。また、これらの値には固有の値が与えられ、接続の存続期間の要求を識別します。
  • 応答では、別のキーを入力します - たとえば、AMPはこれに "_answer"を使います。値は、応答が要求する「_ask」キーの値と一致します。

このような手法を使用すると、新しいリクエストまたは以前のリクエストに対する応答を受け取ったかどうかを判断するための "_ask"キーまたは "_answer"キーがあるかどうかを調べるだけで済みます。

別のトピックでは、asyncGatewayCallsクラスはスレッドベースであってはなりません。スレッドを使用する明白な理由はなく、そうすることで、未定義の動作につながるような方法でTwisted APIを誤用することにもなります。ほとんどのTwisted APIは、reactor.runを呼び出したスレッドでのみ使用できます。唯一の例外はreactor.callFromThreadです。これを使用して、他のスレッドからリアクタスレッドにメッセージを送信できます。 asyncGatewayCallsは、トランスポートに書き込もうとしますが、バッファの破損や送信されるデータの任意の遅延、またはおそらく悪いことにつながります。代わりに、あなたはこのようなasyncGatewayCallsを書くことができます:

from twisted.internet.task import LoopingCall 

class asyncGatewayCalls(object): 
    def __init__(self, rpcfactory): 
     self.rpcfactory = rpcfactory 
     self.remoteMacList = [...] 

    def run(): 
     self._call = LoopingCall(self._pokeMicro) 
     return self._call.start(10) 

    def _pokeMicro(self): 
     while True: 
      mac = self.remoteMacList[...] 
      if mac in self.rpcfactory.references: 
       proto = ... 
       dataToSend = ... 
       proto.transport.write(dataToSend) 
       break 

factory = ... 
r = asyncGatewayCalls(factory) 
r.run() 

reactor.listenTCP(7080, factory) 
reactor.run() 

これはあなたが元asyncGatewayCallsクラスの意図と同じ動作を持っている必要があり、シングルスレッドソリューションを提供します。しかし、呼び出しをスケジュールするためにスレッド内のループでスリープするのではなく、_pokeMicroが10秒ごとに呼び出されることを確認するために、原子炉のスケジューリングAPI(繰り返し呼び出されるようにスケジュールする、より高いレベルのLoopingCallクラスを使用) 。

+0

はい、私はスレッドAPIドキュメント(task.LoopingCall)を読んだ後、同じ結論を出していました。私はテストし、それはかなりうまくいった。あなたの助けをありがとう – Jaime

+1

jsonrpc 'id'フィールドは、どのリクエストがどのリクエストに対応しているかを判断するのに適していませんか? –

+0

JSON-RPC 2.0メッセージを明確に区別できます。通知 - IDフィールドのない要求、応答も同じではありません(結果とエラー)。 –

関連する問題