Twisted XmlStream:如何连接事件? [英] Twisted XmlStream: How to connect to events?
问题描述
我想实现一个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(...)
我的问题:
- 当服务器遇到某个XML标签时,如何订阅一个事件?
// request / content
XPath查询似乎对我来说很好,但是onRequestContent(...)
不会被调用: - ( - 是一个合理的方法子类化
XmlStream
和XmlStreamFactory
?感觉很奇怪,因为XMLServer
订阅由其自己的基类发送的事件,然后将其自身(?)作为xs
参数?!?是否应该使XMLServer
一个普通的类,并有一个XmlStream
对象作为类成员?有没有 - 如何向服务器添加错误处理程序,如
addErrback(...)
在客户端?我担心异常被吞噬(发生在之前),但我看不到从哪里得到一个Deferred
从附加到... - 为什么默认情况下,服务器关闭第一个请求后的连接?我看到
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()
,否则您将收到异常。
我对你的问题的回答:
- 不知道:)
- 这是非常合理的。然而,我通常只是
XmlStream
(它只是继承自协议
),然后使用常规的工厂
对象。 - 使用Twisted(+1)为您服务是件好事。使用上面的方法,您可以在解析和点击元素时触发回调/ errbacks,或者等到您到达XML的末尾,然后将您的回调激发到您的心中。我希望这是有道理的:/
- 我也想知道这一点。我认为这与使用
XmlStream
对象(如Jabber和IRC)的应用程序和协议有关。只需重载onDocumentEnd
,并使其做你想要的。这是OOP的美丽。
参考:
- xml.sax
- iterparse
- twisted.web.sux :Twisted XML SAX解析器。这实际上是
XmlStream
用于解析XML。 - xml.etree.cElementTree.iterparse :这是另一个Stackoverflow问题 - ElementTree迭代策略
- iterparse抛出没有找到元素:第1行,第0列,我不知道为什么 - 我问过类似的问题:)
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:
- 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, butonRequestContent(...)
does not get called :-( - Is subclassing
XmlStream
andXmlStreamFactory
a reasonable approach at all ? It feels weird becauseXMLServer
subscribes to events sent by its own base class and is then passed itself (?) asxs
parameter ?!? Should I rather makeXMLServer
an ordinary class and have anXmlStream
object as class member ? Is there a canonical approach ? - 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 aDeferred
from to attach it to... - Why does the server by default close the connection after the first request ? I see
XmlStream.onDocumentEnd(...)
callingloseConnection()
; 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:
- Don't know :)
- It's very reasonable. However I usually just subclass
XmlStream
(which simply inherits fromProtocol
) and then use a regularFactory
object. - 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 :/
- 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 overloadonDocumentEnd
and make it do what you want it to do. That's the beauty of OOP.
Reference:
- xml.sax
- iterparse
- twisted.web.sux: Twisted XML SAX parser. This is actually what
XmlStream
uses to parse XML. - xml.etree.cElementTree.iterparse: Here's another Stackoverflow question - ElementTree iterparse strategy
- iterparse is throwing 'no element found: line 1, column 0' and I'm not sure why - I've asked a similar question :)
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屋!