2013-10-17 91 views
2

我有下面的代碼使用Python扭圖書館編制:蟒蛇絞JSON解碼

class Cache(protocol.Protocol): 
    def __init__(self, factory): 
     self.factory = factory 

    def dataReceived(self, data): 
     request = json.loads(data) 
     self.factory.handle[request['command']](**request) 
     self.transport.write(data) 

class CacheFactory(protocol.Factory): 
    def buildProtocol(self, addr): 
     return Cache(self) 
    def handle_get(self, **kwargs): 
     print 'get\n', kwargs 
    def handle_set(self, **kwargs): 
     print 'set\n', kwargs 
    def handle_delete(self, **kwargs): 
     print 'delete\n', kwargs 
    handle = { 
     'get': handle_get, 
     'set': handle_set, 
     'delete': handle_delete, 
    } 

reactor.listenTCP(int(sys.argv[1]), CacheFactory()) 
reactor.run() 

我使用運行在客戶端連接遠程登錄:

Trying 127.0.0.1... 
Connected to localhost. 
Escape character is '^]'. 
{"command": "set", "value": 1234567890} 
Connection closed by foreign host. 

拋出一個異常:

Traceback (most recent call last): 
    File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 84, in callWithLogger 
    return callWithContext({"system": lp}, func, *args, **kw) 
    File "/usr/lib/python2.7/dist-packages/twisted/python/log.py", line 69, in callWithContext 
    return context.call({ILogContext: newCtx}, func, *args, **kw) 
    File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 118, in callWithContext 
    return self.currentContext().callWithContext(ctx, func, *args, **kw) 
    File "/usr/lib/python2.7/dist-packages/twisted/python/context.py", line 81, in callWithContext 
    return func(*args,**kw) 
--- <exception caught here> --- 
    File "/usr/lib/python2.7/dist-packages/twisted/internet/selectreactor.py", line 146, in _doReadOrWrite 
    why = getattr(selectable, method)() 
    File "/usr/lib/python2.7/dist-packages/twisted/internet/tcp.py", line 460, in doRead 
    rval = self.protocol.dataReceived(data) 
    File "./server.py", line 18, in dataReceived 
    self.factory.handle[request['command']](**request) 
exceptions.TypeError: handle_set() takes exactly 1 argument (0 given) 

我不明白。行self.factory.handle[request['command']](**request)可能有問題,但在我看來它是正確的 - 它會明確地傳遞self參數(它是一種方法後繼)和解壓縮的請求參數。異常消息說該函數需要1個參數,並且它是一個謊言:),因爲它需要2個參數:self, **kwargs。而且,由於我通過了2,所以我傳遞了0個參數並不是真的。

有人能幫我發現問題嗎?


的情況下,它可以幫助,json的請求被解碼爲:

{u'command': u'set', u'value': 1234567890} 

回答

4

因爲它是現在,在handle_*方法是實例方法,然而handle字典點綁定方法。也就是說,self不會被隱式傳遞。試試這個:

class CacheFactory(protocol.Factory): 
    def buildProtocol(self, addr): 
     return Cache(self) 
    def handle_get(self, **kwargs): 
     print 'get\n', kwargs 
    def handle_set(self, **kwargs): 
     print 'set\n', kwargs 
    def handle_delete(self, **kwargs): 
     print 'delete\n', kwargs 
    def __init__(self, *args, **kwargs): 
     protocol.Factory.__init__(self, *args, **kwargs) 
     self.handle = { 
      'get': self.handle_get, 
      'set': self.handle_set, 
      'delete': self.handle_delete, 
     } 

或者,你可以讓你的handle相同,這樣做:

def dataReceived(self, data): 
     request = json.loads(data) 
     self.factory.handle[request['command']](self.factory, **request) 
     self.transport.write(data) 

或者,你可以採取這種做法,那麼你並不需要一個handle字典無論哪種方式:

def dataReceived(self, data): 
     request = json.loads(data) 
     getattr(self.factory, "handle_%s" % (request['command'],))(**request) 
     self.transport.write(data) 

還要注意你的dataReceived,因爲它是現在,是不安全的,因爲數據包可能會被拆分任意 - 也就是說,您可能無法一次性收到完整的json消息。

+0

哇,謝謝你的獨家解釋。如果使用json格式,你會怎樣保護dataReceived方法? – ducin

+0

@tkoomzaaskz:查看[Twisted FAQ](http://twistedmatrix.com/trac/wiki/FrequentlyAskedQuestions#Whyisprotocol.dataReceivedcalledwithonlypartofthedataataIdledTransport.writewith)。基本上,您必須保留所收到的所有數據的緩衝區,並且您的協議必須能夠知道您何時收到完整的消息。在你的情況下,你可以通過嘗試解析緩衝區並查看是否失敗來做到這一點,我猜,但最好傳遞一些長度信息,例如前4個字節可以是消息大小的網絡字節順序的「uint32」。 – Claudiu

+0

非常感謝! PS我喜歡handler_map-less解決方案最好的 – ducin