简体   繁体   English

如何在Python中通过HTTP从UDP流提供数据?

[英]How to serve data from UDP stream over HTTP in Python?

I am currently working on exposing data from legacy system over the web. 我目前正致力于通过网络公开遗留系统的数据。 I have a (legacy) server application that sends and receives data over UDP. 我有一个(传统)服务器应用程序,通过UDP发送和接收数据。 The software uses UDP to send sequential updates to a given set of variables in (near) real-time (updates every 5-10 ms). 该软件使用UDP在(近)实时(每5-10毫秒更新)发送一组给定变量的顺序更新。 thus, I do not need to capture all UDP data -- it is sufficient that the latest update is retrieved. 因此,我不需要捕获所有UDP数据 - 检索最新更新就足够了。

In order to expose this data over the web, I am considering building a lightweight web server that reads/write UDP data and exposes this data over HTTP. 为了通过Web公开这些数据,我正在考虑构建一个轻量级的Web服务器,它可以读取/写入UDP数据并通过HTTP公开这些数据。

As I am experienced with Python, I am considering to use it. 由于我对Python有经验,我正在考虑使用它。

The question is the following: how can I (continuously) read data from UDP and send snapshots of it over TCP/HTTP on-demand with Python? 问题如下: 我如何(连续)从UDP读取数据并通过TCP / HTTP按需发送它的快照? So basically, I am trying to build a kind of "UDP2HTTP" adapter to interface with the legacy app so that I wouldn't need to touch the legacy code. 所以基本上,我正在尝试构建一种“UDP2HTTP”适配器来与遗留应用程序进行交互,这样我就不需要触及遗留代码了。

A solution that is WSGI compliant would be much preferred. 符合WSGI的解决方案将是更优选的。 Of course any tips are very welcome and MUCH appreciated! 当然,任何提示都非常受欢迎,非常感谢!

Twisted would be very suitable here. Twisted在这里非常合适。 It supports many protocols (UDP, HTTP) and its asynchronous nature makes it possible to directly stream UDP data to HTTP without shooting yourself in the foot with (blocking) threading code. 它支持许多协议(UDP,HTTP),并且它的异步性质使得直接将UDP数据流式传输到HTTP成为可能,而无需使用(阻塞)线程代码将自己射入脚中。 It also support wsgi. 它也支持wsgi。

Here's a quick "proof of concept" app using the twisted framework. 这是使用扭曲框架的快速“概念证明”应用程序。 This assumes that the legacy UDP service is listening on localhost:8000 and will start sending UDP data in response to a datagram containing "Send me data". 这假设传统UDP服务正在侦听localhost:8000,并将开始发送UDP数据以响应包含“发送数据”的数据报。 And that the data is 3 32bit integers. 而且数据是3个32位整数。 Additionally it will respond to an "HTTP GET /" on port 2080. 此外,它将响应端口2080上的“HTTP GET /”。

You could start this with twistd -noy example.py : 你可以用twistd -noy example.py开始这个:

example.py example.py

from twisted.internet import protocol, defer
from twisted.application import service
from twisted.python import log
from twisted.web import resource, server as webserver

import struct

class legacyProtocol(protocol.DatagramProtocol):
    def startProtocol(self):
        self.transport.connect(self.service.legacyHost,self.service.legacyPort)
        self.sendMessage("Send me data")
    def stopProtocol(self):
        # Assume the transport is closed, do any tidying that you need to.
        return
    def datagramReceived(self,datagram,addr):
        # Inspect the datagram payload, do sanity checking.
        try:
            val1, val2, val3 = struct.unpack("!iii",datagram)
        except struct.error, err:
            # Problem unpacking data log and ignore
            log.err()
            return
        self.service.update_data(val1,val2,val3)
    def sendMessage(self,message):
        self.transport.write(message)

class legacyValues(resource.Resource):
    def __init__(self,service):
        resource.Resource.__init__(self)
        self.service=service
        self.putChild("",self)
    def render_GET(self,request):
        data = "\n".join(["<li>%s</li>" % x for x in self.service.get_data()])
        return """<html><head><title>Legacy Data</title>
            <body><h1>Data</h1><ul>
            %s
            </ul></body></html>""" % (data,)

class protocolGatewayService(service.Service):
    def __init__(self,legacyHost,legacyPort):
        self.legacyHost = legacyHost # 
        self.legacyPort = legacyPort
        self.udpListeningPort = None
        self.httpListeningPort = None
        self.lproto = None
        self.reactor = None
        self.data = [1,2,3]
    def startService(self):
        # called by application handling
        if not self.reactor:
            from twisted.internet import reactor
            self.reactor = reactor
        self.reactor.callWhenRunning(self.startStuff)
    def stopService(self):
        # called by application handling
        defers = []
        if self.udpListeningPort:
            defers.append(defer.maybeDeferred(self.udpListeningPort.loseConnection))
        if self.httpListeningPort:
            defers.append(defer.maybeDeferred(self.httpListeningPort.stopListening))
        return defer.DeferredList(defers)
    def startStuff(self):
        # UDP legacy stuff
        proto = legacyProtocol()
        proto.service = self
        self.udpListeningPort = self.reactor.listenUDP(0,proto)
        # Website
        factory = webserver.Site(legacyValues(self))
        self.httpListeningPort = self.reactor.listenTCP(2080,factory)
    def update_data(self,*args):
        self.data[:] = args
    def get_data(self):
        return self.data

application = service.Application('LegacyGateway')
services = service.IServiceCollection(application)
s = protocolGatewayService('127.0.0.1',8000)
s.setServiceParent(services)

Afterthought 事后

This isn't a WSGI design. 这不是WSGI设计。 The idea for this would to use be to run this program daemonized and have it's http port on a local IP and apache or similar to proxy requests. 这样做的想法是使用be来运行这个程序守护进程并在本地IP和apache或类似于代理请求上使用它的http端口。 It could be refactored for WSGI. 它可以为WSGI重构。 It was quicker to knock up this way, easier to debug. 这种方式更快,更容易调试。

The software uses UDP to send sequential updates to a given set of variables in (near) real-time (updates every 5-10 ms). 该软件使用UDP在(近)实时(每5-10毫秒更新)发送一组给定变量的顺序更新。 thus, I do not need to capture all UDP data -- it is sufficient that the latest update is retrieved 因此,我不需要捕获所有UDP数据 - 检索最新更新就足够了

What you must do is this. 你必须做的就是这个。

Step 1. 第1步。

Build a Python app that collects the UDP data and caches it into a file. 构建一个收集UDP数据并将其缓存到文件中的Python应用程序。 Create the file using XML, CSV or JSON notation. 使用XML,CSV或JSON表示法创建文件。

This runs independently as some kind of daemon. 它作为某种守护进程独立运行。 This is your listener or collector. 这是你的听众或收藏家。

Write the file to a directory from which it can be trivially downloaded by Apache or some other web server. 将文件写入一个目录,Apache或其他Web服务器可以从中轻松下载该文件。 Choose names and directory paths wisely and you're done. 明智地选择名称和目录路径,你就完成了。

Done. 完成。

If you want fancier results, you can do more. 如果您想获得更好的结果,您可以做更多。 You don't need to, since you're already done. 你不需要,因为你已经完成了。

Step 2. 第2步。

Build a web application that allows someone to request this data being accumulated by the UDP listener or collector. 构建一个Web应用程序,允许某人请求UDP侦听器或收集器累积此数据。

Use a web framework like Django for this. 使用像Django这样的Web框架。 Write as little as possible. 写得尽量少。 Django can serve flat files created by your listener. Django可以提供由您的侦听器创建的平面文件。

You're done. 你完成了。 Again. 再次。

Some folks think relational databases are important. 有些人认为关系数据库很重要。 If so, you can do this. 如果是这样,你可以这样做。 Even though you're already done. 即使你已经完成了。

Step 3. 第3步。

Modify your data collection to create a database that the Django ORM can query. 修改数据集合以创建Django ORM可以查询的数据库。 This requires some learning and some adjusting to get a tidy, simple ORM model. 这需要一些学习和一些调整来获得一个整洁,简单的ORM模型。

Then write your final Django application to serve the UDP data being collected by your listener and loaded into your Django database. 然后编写最终的Django应用程序来提供监听器收集的UDP数据并加载到Django数据库中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM