持续监控Python中的数据的TCP流式处理

时间:2016-12-14 00:26:52

标签: python python-3.x sockets tcp

我在Python 3.5中编写Python脚本,我有一个主机和端口,我想要做的是创建脚本,以便它不断监视提供的主机数据。数据通过TCP流式传输以xml格式分发,标签标记事件的开始和结束。

所以我要做的是基本上监视TCP事件,以便在xml开始和结束标记之间标记新事件,然后检索事件并在我的脚本中相应地处理它。此外,理想情况下,我需要在几毫秒内访问Feed中的新数据。

Feed是一个分发警报的政府Feed,Feed是streaming1.naad-adna.pelmorex.com,端口是8080,我想要做的是监控此Feed以获取新警报,然后才能访问在Python中相应地发出警报并进行处理。 Feed会每分钟发送一次心跳以指示连接处于活动状态。

我认为最好的选择是使用套接字,但我不确定如何在这个特定的用例中实现它们。我没有太多关于TCP提要的经验,而且在我的特定用例下我无法找到很多关于如何在Python中处理TCP提要的在线,我能够处理xml虽然我能够弄清楚如何从TCP提要中提取它。

任何帮助都将不胜感激。

1 个答案:

答案 0 :(得分:1)

您的问题中提出了一些技术挑战。

首先,是连接服务器和检索数据的简单问题。正如您在下面的connect()中看到的那样,这很简单,只需创建一个套接字(s = socket.socket())并连接它(s.connect(('hostname', port_number)))。

下一个问题是以有用的形式检索数据。套接字本身提供.recv(),但我想要一个类似文件的接口。套接字模块提供了Python独有的方法:.makefile()。 (return s.makefile('rb')

现在我们遇到困难的部分。 XML文档通常每个文件存储一个文档,或者每个TCP传输存储一个文档。因此,文档结尾很容易通过文件结束指示或Content-Length:标题发现。因此,Python XML API都没有一种机制可以在一个文件或一个字符串中处理多个XML文档。我写了xml_partition()来解决这个问题。 xml_partition()使用来自类文件对象的数据,并从流中生成每个XML文档。 (注意:必须将XML文档压在一起。在最终>之后不允许有空格。

最后,有一个简短的测试程序(alerts())连接到流并读取一些XML文档,将每个文档存储到自己的文件中。

这里,整体上是用于从国家警报聚合& amp; amp; amp; amp; amp; amp; amp; amp; amp; Pelmorex的传播系统。

import socket
import xml.etree.ElementTree as ET

def connect():
    'Connect to pelmorex data stream and return a file-like object'
    # Set up the socket
    s = socket.socket()
    s.connect(('streaming1.naad-adna.pelmorex.com', 8080))
    return s.makefile('rb')

# We have to consume the XML data in bits and pieces
# so that we can stop precisely at the boundary between
# streamed XML documents. This function ensures that
# nothing follows a '>' in any XML fragment.
def partition(s, pattern):
    'Consume a file-like object, and yield parts defined by pattern'
    data = s.read(2048)
    while data:
        left, middle, data = data.partition(pattern)
        while left or middle:
            yield left
            yield middle
            left, middle, data = data.partition(pattern)
        data = s.read(2048)

# Split the incoming XML stream into fragments (much smaller
# than an XML document.) The end of each XML document
# is guaranteed to align with the end of a fragment.
# Use an XML parser to determine the actual end of
# a document.  Whenever the parser signals the end
# of an XML document, yield what we have so far and
# start a new parser.
def xml_partition(s):
    'Read multiple XML documents from one data stream'
    parser = None
    for part in partition(s, b'>'):
        if parser is None:
            parser = ET.XMLPullParser(['start', 'end'])
            starts = ends = 0
            xml = []
        xml.append(part)
        parser.feed(part)
        for event, elem in parser.read_events():
            starts += event == "start"
            ends += event == "end"
            if starts == ends > 0:
                # We have reached the end of the XML doc
                parser.close()
                parser = None
                yield b''.join(xml)

# Typical usage:
def alerts():
    for i, xml in enumerate(xml_partition(connect())):
        # The XML is a bytes object that contains the undecoded
        # XML stream. You'll probably want to parse it and
        # somehow display the alert.

        # I'm just saving it to a file.
        with open('alert%d.xml' % i, 'wb') as fp:
            fp.write(xml)
        if i == 3:
            break

def test():
    # A test function that uses multiple XML documents in one
    # file. This avoids the wait for a natural-disaster alert.
    with open('multi.xml', 'rb') as fp:
        print(list(xml_partition(fp)))

alerts()