Найти - Пользователи
Полная версия: Twisted task.loop and Deferred
Начало » Network » Twisted task.loop and Deferred
1
necrozyablo
Продолжаю изучать Twisted в свободное время и вот похоже упёрся в непонимание какой то базовой вещи. Тестовая задача простая написать сервер с клиентом, которые обмениваются данными раз в секунду при этом коннект авторизированный.

Код клиента конектится раз в секунду.
# -*- coding: utf-8 -*-

from twisted.spread import pb
from twisted.internet import reactor, task
from twisted.cred import credentials

class login_send:

def __init__(self):
self.count=0
self.timeout = 1.0
self.factory = pb.PBClientFactory()
reactor.connectTCP("localhost", 8800, self.factory)

def testTimeout(self):
self.count+=1
print self.count

def1 = self.factory.login(credentials.UsernamePassword("test1","bbb"))
def1.addCallbacks(self.good_connected, self.bad_connected)
def1.addCallback(self.send_data)
def1.addCallback(self.print_data)
def1.addErrback(self.disconnect)
if self.count>10:def1.addBoth(self.disconnect)

def start(self):
l = task.LoopingCall(self.testTimeout)
l.start(self.timeout)
reactor.run()

def good_connected(self, perspective):
print 'good login and password', perspective
return perspective

def bad_connected(self, perspective):
print 'bad login or password', perspective
return perspective

def send_data(self, perspective):
print 'send'
return perspective.callRemote("foo", self.count)

def print_data(self, data):
print 'data ', data

def disconnect(self, perspective):
print 'disconnect'
reactor.stop()

if __name__ == "__main__":
st=login_send()
st.start()
Код сервера если авторизация прошла то возвращает квадрат числа.

from zope.interface import implements

from twisted.spread import pb
from twisted.cred import checkers, portal
from twisted.internet import reactor

class MyPerspective(pb.Avatar):
def __init__(self, name):
self.name = name
def perspective_foo(self, arg):
print "I am", self.name, "perspective_foo(",arg,") called on", self
return arg*arg

class MyRealm:
implements(portal.IRealm)
def requestAvatar(self, avatarId, mind, *interfaces):
if pb.IPerspective not in interfaces:
raise NotImplementedError
return pb.IPerspective, MyPerspective(avatarId), lambda:None

p = portal.Portal(MyRealm())
c = checkers.InMemoryUsernamePasswordDatabaseDontUse(test1="bbb",
user2="pass2")
p.registerChecker(c)
reactor.listenTCP(8800, pb.PBServerFactory(p))
reactor.run()
Написал первично такой код, но он ИМХО ужасен с идиологической точки зрения т.к. каждый раз приходится авторизироваться. Как сделать чтобы клиент также раз в секунду продолжал отправлять данные серверу, но при этом авторизировался 1 раз?
Андрей Светлов
Не рвите соединение, вот и всё. Или вы не понимаете, как на клиенте писать reactor.callLater(10, self.send_data) ?
necrozyablo
Андрей Светлов
Не рвите соединение, вот и всё. Или вы не понимаете, как на клиенте писать reactor.callLater(10, self.send_data) ?
Честно я довольного много всего пробовал, но всегда вылазят ошибки с которыми я не могу справится. Отсюда чувство что я не понимаю какой то базовой основы.

Вот например почему я не могу сделать вот так:
        def1.addCallback(self.send_data)
def1.addCallback(self.print_data)
def1.addCallback(self.send_data)
def1.addCallback(self.print_data)
Во второй send уже приходит None, оно и ясно т.к. self.print_data ничего посылает в return. Но если я сделаю return data то второй send_data всё равно умрёт со словами ‘int’ object has no attribute ‘callRemote’
Т.е. фактически я не понимаю как просто пошагово 10 раз друг за другом вызвать один и тот же addCallback + perspective.callRemote(“foo”, self.count) возвращает deferred и его результат в виде числа, я вижу уже только в следущем addCallback
bw
Забросил я Twisted, даже как-то стыдно…
  def1.addCallback(self.send_all_data)

def send_all_data(self, p):
self.send_data(p).addCallback(self.print_data)
self.send_data(p).addCallback(self.print_data)
self.send_data(p).addCallback(self.print_data)
Если важно поймать завершение всех этих “задач”, то…
defers = [
self.send_data(p).addCallback(self.print_data),
self.send_data(p).addCallback(self.print_data),
self.send_data(p).addCallback(self.print_data)
]

group_defer = twisted.internet.defer.DeferredList(defers)
# или -- group_defer = twisted.internet.defer.gatherResults(defers)
# тут уж сам думай
Порядок выполнения запросов, кстати, в данном случае не обязательно будет соотв. порядку в списке. Сохрани ты этот корневой объект для удалённых вызовов где-нибудь (в self :-) и используй повторно, не обязательно же его передавать как аргумент callback'ам, хотя возможно и так работать…

self.data_stack = ['data1', 'data2']

def send_data(self, perspective, data):
return perspective.callRemote(data, self.count)

def some_behavior(self, remote):
data = self.data_stack.pop(0)
return\
.addCallback(lambda _: self.send_data(remote, data))\
.addCallback(self.print_data)\
.addCallback(lambda _: self.some_behavior(remote))

def1.addCallback(self.some_behavior)
Не стоит не обдумав копировать мой код, это набросок, давно не практиковал же. Там где “lambda _”, это значит мне плевать на входные данные. И разрывать соединение не надо, блин.

..bw
necrozyablo
Хм ваши коды работают. Предложение не дисконектится, и вынести в self мне понятны собственно в самом начале свои не рабочие попытки я начал с
    def testTimeout(self):
# no connection -- create one.
if not self.connection:
self.assign_connection()
# cached connection exists, call send_data manually.
elif self.count > 10:
self.disconnect(self.connection)
else:
self.connection.addCallback(self.send_data).addCallback(self.print_data)
self.count+=1

def assign_connection(self):
# cache the connection.
self.connection = self.factory.login(credentials.UsernamePassword("test1","bbb"))
def send_data(self):
return self.connection.callRemote("foo", self.count)
def print_data(self, data):
print 'print_data', data
Мне казалось вполне логичным создать соединение, сохранить его и потом пушить в него данные
На что собсвтенно я получил, тоже что и в самом начале -смерть на 2 итерации из-за того что в send_data приходит None вместо <twisted.spread.pb.RemoteReference instance at 0x0000000002C8F148>
Собственно сейчас я сделал вот так:
 
def send_data(self, p):
print 'p in send_data', p
if self.count==1:
self.remote_caller=p
else:
p=self.remote_caller
return p.callRemote("foo", self.count)
Т.е. при первой итерации я тупо запомнил этот самый <twisted.spread.pb.RemoteReference instance at 0x0000000002C8F148> в self и дальше его вызываю, вроде работает.
Как думаете это нормально? и вы об этом говорили в “Сохрани ты этот корневой объект для удалённых вызовов где-нибудь (в self :-) и используй повторно”
bw
Не красиво, но сгодится для первого раза.

..bw
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB