Как обслуживать данные из потока UDP через HTTP в Python? - PullRequest
6 голосов
/ 22 сентября 2010

В настоящее время я работаю над раскрытием данных из устаревшей системы через Интернет. У меня есть (устаревшее) серверное приложение, которое отправляет и получает данные по UDP. Программное обеспечение использует UDP для отправки последовательных обновлений для данного набора переменных в (почти) режиме реального времени (обновления каждые 5-10 мс). таким образом, мне не нужно захватывать все данные UDP - достаточно получить последнее обновление.

Чтобы представить эти данные через Интернет, я рассматриваю возможность создания облегченного веб-сервера, который считывает / записывает данные UDP и предоставляет эти данные через HTTP.

Поскольку у меня есть опыт работы с Python, я собираюсь использовать его.

Вопрос заключается в следующем: как я могу (непрерывно) читать данные из UDP и отправлять их снимки по TCP / HTTP по запросу с Python? Итак, в основном я пытаюсь создать адаптера "UDP2HTTP" для взаимодействия с унаследованным приложением, чтобы мне не нужно было трогать унаследованный код.

Было бы предпочтительным решение, совместимое с WSGI. Конечно, любые советы очень приветствуются и очень ценятся!

Ответы [ 3 ]

6 голосов
/ 22 сентября 2010

Витая будет очень подходящим здесь.Он поддерживает множество протоколов (UDP, HTTP), а его асинхронный характер позволяет напрямую передавать данные UDP в HTTP, не выпуская себя из рук (блокируя) код потока.Также поддерживается wsgi.

5 голосов
/ 22 сентября 2010

Вот быстрое приложение для проверки концепции с использованием витой среды. Это предполагает, что устаревшая служба UDP прослушивает localhost: 8000 и начнет отправку данных UDP в ответ на дейтаграмму, содержащую «Отправить мне данные». И это данные 3 32-битных целых. Кроме того, он ответит на «HTTP GET /» на порту 2080.

Вы можете начать это с twistd -noy 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)

* Запоздалая мысль 1011 *

Это не дизайн WSGI. Идея для этого заключается в том, чтобы запустить эту программу демонизированно и иметь ее http-порт на локальном IP и apache или аналогично запросам прокси. Это может быть реорганизовано для WSGI. Так было быстрее разобраться, легче отладить.

3 голосов
/ 22 сентября 2010

Программное обеспечение использует UDP для отправки последовательных обновлений для данного набора переменных в (почти) режиме реального времени (обновления каждые 5-10 мс). таким образом, мне не нужно захватывать все данные UDP - достаточно получить последнее обновление

То, что вы должны сделать, это.

Шаг 1.

Создайте приложение Python, которое собирает данные UDP и кэширует их в файл. Создайте файл, используя нотацию XML, CSV или JSON.

Это работает независимо как некий демон. Это ваш слушатель или коллекционер.

Записать файл в каталог, из которого он может быть тривиально загружен Apache или другим веб-сервером. Мудро выбирайте имена и пути к каталогам, и все готово.

Готово.

Если вы хотите более необычные результаты, вы можете сделать больше. Вам не нужно, так как вы уже сделали.

Шаг 2.

Создайте веб-приложение, которое позволяет кому-либо запрашивать накопление данных слушателем или сборщиком UDP.

Используйте для этого веб-фреймворк, такой как Django. Пишите как можно меньше. Django может обслуживать плоские файлы, созданные вашим слушателем.

Вы сделали. Опять же.

Некоторые люди считают, что реляционные базы данных важны. Если это так, вы можете сделать это. Даже если вы уже сделали.

Шаг 3.

Измените вашу коллекцию данных, чтобы создать базу данных, к которой может обращаться Django ORM. Это требует некоторого обучения и некоторой корректировки, чтобы получить аккуратную, простую модель ORM.

Затем напишите ваше окончательное приложение Django для обработки данных UDP, которые собираются вашим слушателем и загружаются в базу данных Django.

...