Уведомления

Группа в Telegram: @pythonsu

#1 Май 15, 2008 22:50:24

init
От:
Зарегистрирован: 2008-05-15
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

Ретрансляция udp multicast в http на Twisted

Необходимо слушать порт и принимать с мультикаста udp-пакеты, которые по запросу отдавать по http. Есть что-то вроде этого:

class MyRequestHandler(http.Request):
    def process(self):
        #здесь отдаем пакеты клиенту
class MyHttp(http.HTTPChannel):
    requestFactory = MyRequestHandler
class MyHttpFactory(http.HTTPFactory):
    protocol = MyHttp
reactor.listenTCP(8080, MyHttpFactory())
class MulticastServerUDP(DatagramProtocol):
    def startProtocol(self):
        self.transport.joinGroup('229.254.1.11')
    def datagramReceived(self, data, address):
        # здесь получаем пакеты
reactor.listenMulticast(9000, MulticastServerUDP())
reactor.run()
Необходимо сделать так, чтобы при запуске process() он каким-то образом сигнализировал о своем появлении datagramReceived() и последний при получении пакета отдавал его в self.write у process(). То есть при каждом вызове datagramReceived во все активные http-соединения писался бы полученный пакет. Как это сделать?
Читаю про Producer/Consumer, Deferred, но так и не понял что мне здесь нужно.



Отредактировано (Май 15, 2008 22:55:04)

Офлайн

#2 Май 16, 2008 13:30:25

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Ретрансляция udp multicast в http на Twisted

непонятно. У тебя персистентные http соединения? Если нет - соединение закроется сразу же после отдавания пакета. И http connection обычно управляется со стороны клиента, т.е. отдает что попросили без излишней самодеятельности. В AJAX есть способы обойти, но они зависят от используемой библиотеки.

Что-то, кажется, не совсем “чисто” в архитектуре. Либо приведенный пример слишком мало ее раскрывает.



Офлайн

#3 Май 16, 2008 13:36:33

init
От:
Зарегистрирован: 2008-05-15
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

Ретрансляция udp multicast в http на Twisted

Соединение открывается и по нему непрерывно отдаются данные. Я так понял нужно на каждый вызов datagramReceived вызывать MyRequestHandler.write(data), так соединение не закрывается. Проблема не в отдаче - это все нормально работает, а как связать получение пакета по udp и отдачу его в write()



Офлайн

#4 Май 16, 2008 14:23:04

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Ретрансляция udp multicast в http на Twisted

экземпляр MyHttpFactory - глобальный.
В HTTPChannel (который на самом деле протокол) на allContentReceived/connectionLost регистрируешь/прибиваешь протокол в своем списке протоколов для фабрики. allContentReceived - потому что на этот момент уже все передано и настроено.
В datagramReceived проходишься по всем протоколам в фабрике и каждому говоришь proto.transport.write(…).

Повторяю: метод кривоват. Не уверен, что нормально будет работать со всеми браузерами. Конечно же, тебе он нужен не для браузера - но тогда зачем http. Еще сильно не уверен в том, что такие http будут нормально проходить через proxy.



Офлайн

#5 Май 20, 2008 20:50:13

init
От:
Зарегистрирован: 2008-05-15
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

Ретрансляция udp multicast в http на Twisted

Вообще клиент не браузер, а видео-плеер, работа через прокси не нужна. Попробовал сделать вот так, вроде как работает.

class MyRequestHandler(http.Request):
    def out(self, data):
        #этот callback вызывается на каждую datagramReceived, в нем же в deffered добавляем снова себя
        self.write(data)
        if self.transport.connected:
            d = defer.Deferred()
            d.addCallback(self.out)
            self.mserver.deferreds.append(d)
        else:
            self.finish()
    def process(self):
        #соединение установлено, создаем первый callback
        d = defer.Deferred()
        d.addCallback(self.out)
        self.mserver.deferreds.append(d)
 
class MyHttp(http.HTTPChannel):
    requestFactory = MyRequestHandler
 
class MyHttpFactory(http.HTTPFactory):
    protocol = MyHttp
  
class MulticastServerUDP(DatagramProtocol):
    def __init__(self):
        #deffereds - это список объектов Deffered, в каждый из которых добавляются колбэки при подключении новых клиентов по http
        self.deffereds = []
    def startProtocol(self):
        self.transport.joinGroup('229.254.1.11')
 
    def datagramReceived(self, data, address):
        # получаем пакет и посылаем его по всем затребовавшим callback'ам
        deferreds = copy(self.deferreds)
        self.deferreds = []
        for d in deferreds:
            d.callback(data)
mcast = MulticastServerUDP()
reactor.listenMulticast(9000, mcast)
mhttp = MyHttpFactory()
mhttp.mserver = mcast
reactor.listenTCP(8080, mhttp)
reactor.run()

Оцените кривость решения?



Отредактировано (Май 20, 2008 20:53:29)

Офлайн

#6 Май 20, 2008 22:13:48

init
От:
Зарегистрирован: 2008-05-15
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

Ретрансляция udp multicast в http на Twisted

Что-то я перечитал еще раз ваш совет и подумал, что явно перемудрил с Deferred. Переписал так:

class MyRequestHandler(http.Request):
    def process(self):
        #добавляем себя в список к MulticastServerUDP
        self.transport.protocol.factory.mserver.outputs.append(self)
 
class MyHttp(http.HTTPChannel):
    requestFactory = MyRequestHandler
 
class MyHttpFactory(http.HTTPFactory):
    protocol = MyHttp
  
class MulticastServerUDP(DatagramProtocol):
    def __init__(self):
        #outputs - список экземпляров http.Request, в которые нужно писать получаемые данные
        self.outputs = []
 
    def startProtocol(self):
        self.transport.joinGroup('229.254.1.11')
 
    def datagramReceived(self, data, address):
        # получаем пакет и посылаем его по всем затребовавшим
        for out in outputs:
            out.write(data)
 
mcast = MulticastServerUDP()
reactor.listenMulticast(9000, mcast)
 
mhttp = MyHttpFactory()
mhttp.mserver = mcast
reactor.listenTCP(8080, mhttp)
 
reactor.run()



Отредактировано (Май 20, 2008 22:15:23)

Офлайн

#7 Май 21, 2008 13:11:46

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Ретрансляция udp multicast в http на Twisted

Второй пример гораздо лучше. Deferred следует использовать правильно и только там, где действительно необходимо. Но все же стоит вставить удаление из MulticastServerUDP.outputs завершившегося канала. Причина завершения может быть самой разной, начиная с тривиального обрыва соединения.



Офлайн

#8 Май 21, 2008 13:31:13

init
От:
Зарегистрирован: 2008-05-15
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

Ретрансляция udp multicast в http на Twisted

Удаление сделал, просто забыл его скопировать:

class MyRequestHandler(http.Request):
def connectionLost(self, reason):
self.transport.protocol.factory.mserver. outputs.remove(self)
Только вот что странно, через сутки работы это приложение распухает до 1гб в памяти. Вроде все объекты удаляются, никак не пойму что там копится..



Офлайн

#9 Май 21, 2008 14:18:33

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Ретрансляция udp multicast в http на Twisted

Смотреть через модуль gc.
Для начала можно форсировано вызывать gc.collect() (например, каждую минуту).



Офлайн

#10 Май 21, 2008 17:05:11

init
От:
Зарегистрирован: 2008-05-15
Сообщения: 12
Репутация: +  0  -
Профиль   Отправить e-mail  

Ретрансляция udp multicast в http на Twisted

Не помогает. collect() возвращает всегда одно и то же число 17, память продолжает съедаться. В gc.garbage пусто.



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version