Twisted XmlStream:如何连接事件? [英] Twisted XmlStream: How to connect to events?

查看:134
本文介绍了Twisted XmlStream:如何连接事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个Twisted服务器,期望XML请求并发送XML响应:

 <请求类型='type 01'>< content>一些请求内容< / content>< / request> 
< response type ='type 01'>< content>某些响应内容< / content>< / response>
< request type ='type 02'>< content>其他请求内容< / content>< / request>
< response type ='type 02'>< content>其他响应内容< / content>< / response>

我创建了一个Twisted客户端&服务器之前交换了简单的字符串,并尝试将其扩展为使用XML,但我似乎无法弄清楚如何设置它全部正确。



client.py:

 #!/ usr / bin / env python 
#encoding:utf-8

来自twisted.internet import reactor
来自twisted.internet.endpoints导入TCP4ClientEndpoint,connectProtocol
from twisted.words.xish.domish import元素,IElement
来自twisted.words.xish.xmlstream import XmlStream

class XMLClient(XmlStream):

def sendObject(self,obj):
if IElement.providedBy(obj):
print[TX]:%s% obj.toXml()
else:
print[TX]:%s%obj
self.send(obj)

def gotProtocol(p)
request = Element((None,'request'))
request ['type'] ='type 01'
request.addElement('content')。addContent('some request content')
p.sendObject(request)

request = Element((None,'request'))
请求['type'] ='type 02'
request.addElement('content')。addContent('other request content')

reactor.callLater(1,p.sendObject ,request)
reactor.callLater(2,p.transport.loseConnection)

endpoint = TCP4ClientEndpoint(reactor,'127.0.0.1',12345)
d = connectProtocol(endpoint, XMLClient())
d.addCallback(gotProtocol)

来自twisted.python import log
d.addErrback(log.err)

反应堆。 run()

与前面提到的基于字符串的方法一样,客户端空闲直到 CTRL + C 。一旦我这样做,它将从Twisted XMPP示例中获得一些/很多灵感



server.py:



< pre class =lang-py prettyprint-override> #!/ usr / bin / env python
#encoding:utf-8

from twisted.internet import来自twisted.internet.endpoints的反应堆
导入TCP4ServerEndpoint
from twisted.words.xish.xmlstream import XmlStream,XmlStreamFactory
from twisted.words.xish.xmlstream import STREAM_CONNECTED_EVENT,STREAM_START_EVENT,STREAM_END_EVENT

REQUEST_CONTENT_EVENT = intern(// request / content)

class XMLServer(XmlStream):
def __init __(self):
XmlStream .__ init __ )
self.addObserver(STREAM_CONNECTED_EVENT,self.onConnected)
self.addObserver(STREAM_START_EVENT,self.onRequest)
self.addObserv呃(STREAM_END_EVENT,self.onDisconnected)
self.addObserver(REQUEST_CONTENT_EVENT,self.onRequestContent)

def onConnected(self,xs):
print'onConnected(...) '

def onDisconnected(self,xs):
print'onDisconnected(...)'

def onRequest(self,xs):
打印'onRequest(...)'

def onRequestContent(self,xs):
print'onRequestContent(...)'

class XMLServerFactory(XmlStreamFactory )
protocol = XMLServer

endpoint = TCP4ServerEndpoint(reactor,12345,interface ='127.0.0.1')
endpoint.listen(XMLServerFactory())
反应堆.run()

client.py

  TX [127.0.0.1]:< request type ='type 01'>< content>一些请求内容< ; /内容>< /请求> 
TX [127.0.0.1]:< request type ='type 02'>< content>其他请求内容< / content>< / request>

server.py output:


  onConnected(...)
onRequest(...)
onDisconnected(...)

我的问题:


  1. 当服务器遇到某个XML标签时,如何订阅一个事件? // request / content XPath查询似乎对我来说很好,但是 onRequestContent(...)不会被调用: - (

  2. 是一个合理的方法子类化 XmlStream XmlStreamFactory ?感觉很奇怪,因为 XMLServer 订阅由其自己的基类发送的事件,然后将其自身(?)作为 xs 参数?!?是否应该使 XMLServer 一个普通的类,并有一个 XmlStream 对象作为类成员?有没有

  3. 如何向服务器添加错误处理程序,如 addErrback(...)在客户端?我担心异常被吞噬(发生在之前),但我看不到从哪里得到一个 Deferred 从附加到...

  4. 为什么默认情况下,服务器关闭第一个请求后的连接?我看到 XmlStream.onDocumentEnd(...) code> loseConnection();我可以o告诉你这种方法,但是我不知道是否有一个关闭的原因我没看到。直到所有必要的沟通已经进行之前,是否打开连接是不是正常的方法?

我希望这个帖子不太具体;通过网络交谈XML是常见的,但是尽管搜索了一天半,但我无法找到任何Twisted XML服务器示例。也许我可以把它变成一个对于将来有类似问题的任何人的启动。

解决方案

唯一相关组件 XmlStream 是SAX解析器。以下是我使用 XmlStream 和仅解析XML解析函数实现异步SAX解析器的方法:



server.py

 从twisted.words.xish.domish导入元素
from twisted.words。 xish.xmlstream import XmlStream

class XmlServer(XmlStream):
def __init __(self):
XmlStream .__ init __(self)#可能不必要

def dataReceived(self,data):
重载这个函数,只需将输入的数据传递到XML解析器
try:
self.stream.parse(data)# self.stream在self._initializestream()被调用
之后创建,除了Exception为e:
self._initializeStream()#重新启动DOM,以便其他XML可以被解析

def onDocumentStart(self,elementRoot):
根标记已解析
print('根标签:{0}'。format(elementRoot.name))
print('Attributes:{0}'。format(elementRoot.attributes))

def onElement(self,element):
解析了
print('\\\
Element tag:{0}'。format(element.name))
print('Element attributes:{0}'。format(element.attributes))
print('Element content:{0}'。format(str(element)))

def onDocumentEnd(self):
解析已经完成,你应该立即发送你的回复
response = domish.Element(('','response'))
response ['type'] ='type 01'
response.addElement('content',content ='some response content')
self.send(response.toXml())

然后,您创建一个Factory类,将生成此协议(您已经展示了您的能力)。基本上,您将从 onDocumentStart onElement 函数中的XML获取所有信息,并且当您达到结束(即 onDocumentEnd ),您将根据解析的信息发送回复。另外,在解析每个XML消息之后,请确保您调用 self._initializestream(),否则您将收到异常。



我对你的问题的回答:


  1. 不知道:)

  2. 这是非常合理的。然而,我通常只是 XmlStream (它只是继承自协议),然后使用常规的工厂对象。

  3. 使用Twisted(+1)为您服务是件好事。使用上面的方法,您可以在解析和点击元素时触发回调/ errbacks,或者等到您到达XML的末尾,然后将您的回调激发到您的心中。我希望这是有道理的:/

  4. 我也想知道这一点。我认为这与使用 XmlStream 对象(如Jabber和IRC)的应用程序和协议有关。只需重载 onDocumentEnd ,并使其做你想要的。这是OOP的美丽。

参考:





PS



你的问题很常见简单的解决(至少在我看来),所以不要杀了自己试图学习事件抽奖模式。实际上,你似乎有一个很好的处理回调和错误(aka Deferred ),所以我建议你坚持,避免调度员。


I would like to implement a Twisted server that expects XML requests and sends XML responses in return:

<request type='type 01'><content>some request content</content></request>
<response type='type 01'><content>some response content</content></response>
<request type='type 02'><content>other request content</content></request>
<response type='type 02'><content>other response content</content></response>

I have created a Twisted client & server before that exchanged simple strings and tried to extend that to using XML, but I can't seem to figure out how to set it all up correctly.

client.py:

#!/usr/bin/env python
# encoding: utf-8

from twisted.internet             import reactor
from twisted.internet.endpoints   import TCP4ClientEndpoint, connectProtocol
from twisted.words.xish.domish    import Element, IElement
from twisted.words.xish.xmlstream import XmlStream

class XMLClient(XmlStream):

    def sendObject(self, obj):
        if IElement.providedBy(obj):
            print "[TX]: %s" % obj.toXml()
        else:
            print "[TX]: %s" % obj
        self.send(obj)

def gotProtocol(p):
    request = Element((None, 'request'))
    request['type'] = 'type 01'
    request.addElement('content').addContent('some request content')
    p.sendObject(request)

    request = Element((None, 'request'))
    request['type'] = 'type 02'
    request.addElement('content').addContent('other request content')

    reactor.callLater(1, p.sendObject, request)
    reactor.callLater(2, p.transport.loseConnection)

endpoint = TCP4ClientEndpoint(reactor, '127.0.0.1', 12345)
d = connectProtocol(endpoint, XMLClient())
d.addCallback(gotProtocol)

from twisted.python import log
d.addErrback(log.err)

reactor.run()

As in the earlier string-based approach mentioned, the client idles until CTRL+C. Once I have this going, it will draw some / a lot of inspiration from the Twisted XMPP example.

server.py:

#!/usr/bin/env python
# encoding: utf-8

from twisted.internet             import reactor
from twisted.internet.endpoints   import TCP4ServerEndpoint
from twisted.words.xish.xmlstream import XmlStream, XmlStreamFactory
from twisted.words.xish.xmlstream import STREAM_CONNECTED_EVENT, STREAM_START_EVENT, STREAM_END_EVENT

REQUEST_CONTENT_EVENT = intern("//request/content")

class XMLServer(XmlStream):
    def __init__(self):
        XmlStream.__init__(self)
        self.addObserver(STREAM_CONNECTED_EVENT, self.onConnected)
        self.addObserver(STREAM_START_EVENT,     self.onRequest)
        self.addObserver(STREAM_END_EVENT,       self.onDisconnected)
        self.addObserver(REQUEST_CONTENT_EVENT,  self.onRequestContent)

    def onConnected(self, xs):
        print 'onConnected(...)'

    def onDisconnected(self, xs):
        print 'onDisconnected(...)'

    def onRequest(self, xs):
        print 'onRequest(...)'

    def onRequestContent(self, xs):
        print 'onRequestContent(...)'

class XMLServerFactory(XmlStreamFactory):
    protocol = XMLServer

endpoint = TCP4ServerEndpoint(reactor, 12345, interface='127.0.0.1')
endpoint.listen(XMLServerFactory())
reactor.run()

client.py output:

TX [127.0.0.1]: <request type='type 01'><content>some request content</content></request>
TX [127.0.0.1]: <request type='type 02'><content>other request content</content></request>

server.py output:

onConnected(...)
onRequest(...)
onDisconnected(...)

My questions:

  1. How do I subscribe to an event fired when the server encounters a certain XML tag ? The //request/content XPath query seems ok to me, but onRequestContent(...) does not get called :-(
  2. Is subclassing XmlStream and XmlStreamFactory a reasonable approach at all ? It feels weird because XMLServer subscribes to events sent by its own base class and is then passed itself (?) as xs parameter ?!? Should I rather make XMLServer an ordinary class and have an XmlStream object as class member ? Is there a canonical approach ?
  3. How would I add an error handler to the server like addErrback(...) in the client ? I'm worried exceptions get swallowed (happened before), but I don't see where to get a Deferred from to attach it to...
  4. Why does the server by default close the connection after the first request ? I see XmlStream.onDocumentEnd(...) calling loseConnection(); I could override that method, but I wonder if there's a reason for the closing I don't see. Is it not the 'normal' approach to leave the connection open until all communication necessary for the moment has been carried out ?

I hope this post isn't considered too specific; talking XML over the network is commonplace, but despite searching for a day and a half, I was unable to find any Twisted XML server examples. Maybe I manage to turn this into a jumpstart for anyone in the future with similar questions...

解决方案

The "only" relevant component of XmlStream is the SAX parser. Here's how I've implemented an asynchronous SAX parser using XmlStream and only the XML parsing functions:

server.py

from twisted.words.xish.domish import Element
from twisted.words.xish.xmlstream import XmlStream

class XmlServer(XmlStream):
    def __init__(self):
        XmlStream.__init__(self)    # possibly unnecessary

    def dataReceived(self, data):
        """ Overload this function to simply pass the incoming data into the XML parser """
        try:
            self.stream.parse(data)     # self.stream gets created after self._initializestream() is called
        except Exception as e:
            self._initializeStream()    # reinit the DOM so other XML can be parsed

    def onDocumentStart(self, elementRoot):
        """ The root tag has been parsed """
        print('Root tag: {0}'.format(elementRoot.name))
        print('Attributes: {0}'.format(elementRoot.attributes))

    def onElement(self, element):
        """ Children/Body elements parsed """
        print('\nElement tag: {0}'.format(element.name))
        print('Element attributes: {0}'.format(element.attributes))
        print('Element content: {0}'.format(str(element)))

    def onDocumentEnd(self):
        """ Parsing has finished, you should send your response now """
        response = domish.Element(('', 'response'))
        response['type'] = 'type 01'
        response.addElement('content', content='some response content')
        self.send(response.toXml())

Then you create a Factory class that will produce this Protocol (which you've demonstrated you're capable of). Basically, you will get all your information from the XML in the onDocumentStart and onElement functions and when you've reached the end (ie. onDocumentEnd) you will send a response based on the parsed information. Also, be sure you call self._initializestream() after parsing each XML message or else you'll get an exception. That should serve as a good skeleton for you.

My answers to your questions:

  1. Don't know :)
  2. It's very reasonable. However I usually just subclass XmlStream (which simply inherits from Protocol) and then use a regular Factory object.
  3. This is a good thing to worry about when using Twisted (+1 for you). Using the approach above, you could fire callbacks/errbacks as you parse and hit an element or wait till you get to the end of the XML then fire your callbacks to your hearts desire. I hope that makes sense :/
  4. I've wondered this too actually. I think it has something to do with the applications and protocols that use the XmlStream object (such as Jabber and IRC). Just overload onDocumentEnd and make it do what you want it to do. That's the beauty of OOP.

Reference:

PS

Your problem is quite common and very simple to solve (at least in my opinion) so don't kill yourself trying to learn the Event Dipatcher model. Actually it seems you have a good handle on callbacks and errbacks (aka Deferred), so I suggest you stick to those and avoid the dispatcher.

这篇关于Twisted XmlStream:如何连接事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆