2010-12-08 2 views
1

안녕하세요, 나는 트위스트 jsonrpc 서버에 rpc 전화를 만드는 여러 마이크로 컨트롤러를 제공하기 위해 트위스트를 기반으로 rpc 서버를 개발하고 있습니다. 그러나 응용 프로그램은 또한 서버가 언제든지 각 마이크로에 정보를 보내도록 요구 했으므로 문제는 마이크로부터 원격 jsonrpc 호출의 응답을 서버 jsonrpc 요청과 혼동하지 않도록하는 좋은 방법 일 수 있습니다. 사용자.두 가지 방법을 구현하는 방법 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 클래스는 스레드 기반이 아니어야합니다. 스레드를 사용하는 분명한 이유가 없으며 그렇게함으로써 정의되지 않은 동작으로 이어지는 방식으로 트위스티드 API를 오용하고 있습니다. 대부분의 Twisted API는 reactor.run을 호출 한 스레드에서만 사용할 수 있습니다. 유일한 예외는 reactor.callFromThread입니다. 다른 스레드에서 원자로 스레드로 메시지를 보내는 데 사용할 수 있습니다. asyncGatewayCalls은 전송에 쓰려고 시도하지만 전송되는 데이터에 버퍼 손상이나 임의의 지연이 발생하거나 악화 될 수 있습니다. 대신,이 같은 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() 

. 호출을 스케쥴하기 위해 쓰레드의 루프에서 자고있는 대신, (반복적으로 호출되는 것을 스케쥴링하는 더 높은 레벨의 LoopingCall 클래스를 통해) reactor의 스케쥴링 API를 사용하여 _pokeMicro이 매 10 초마다 호출되도록합니다 .

+0

예, 몇 시간 전에 스레드 API 설명서 (task.LoopingCall)를 읽은 후 동일한 결론을 내었습니다. 테스트를 거쳤고 꽤 잘 돌아갔다. 도움을 주셔서 감사합니다. – Jaime

+1

jsonrpc 'id'필드가 어느 요청에 대한 응답인지 판단하기에 적합하지 않습니까? –

+0

JSON-RPC 2.0 메시지를 명확하게 구분할 수 있습니다. 알림 - id 필드가없는 요청도 응답이 같지 않습니다 (결과 및 오류). –

관련 문제