Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何正确触发python twisted传输?_Python_Twisted - Fatal编程技术网

如何正确触发python twisted传输?

如何正确触发python twisted传输?,python,twisted,Python,Twisted,我被要求编写一个连接到服务器的类,异步地向服务器发送各种命令,然后将返回的数据提供给客户端。我被要求用Python来做这件事,Python对我来说是一种新语言。我开始四处挖掘,发现了Twisted框架,它提供了一些非常好的抽象(协议、协议工厂、反应堆),可以做很多事情,如果我要推出自己的基于套接字的应用程序,我就必须做这些事情。考虑到我必须解决的问题,这似乎是一个正确的选择 我已经浏览了很多web上的示例(大部分),但是我仍然没有看到一个创建一个客户端的好例子,该客户端将通过网络发送多个命令,并

我被要求编写一个连接到服务器的类,异步地向服务器发送各种命令,然后将返回的数据提供给客户端。我被要求用Python来做这件事,Python对我来说是一种新语言。我开始四处挖掘,发现了Twisted框架,它提供了一些非常好的抽象(协议、协议工厂、反应堆),可以做很多事情,如果我要推出自己的基于套接字的应用程序,我就必须做这些事情。考虑到我必须解决的问题,这似乎是一个正确的选择

我已经浏览了很多web上的示例(大部分),但是我仍然没有看到一个创建一个客户端的好例子,该客户端将通过网络发送多个命令,并且我维护我创建的连接。在本例中,服务器(我无法控制)在发送响应后不会断开连接。那么,设计客户机的正确方法是什么,这样我就可以用各种方式逗服务器开心了

现在我这样做:

class TestProtocol(Protocol)
    def connectionMade(self):
         self.transport.write(self.factory.message)

class TestProtocolFactory(Factory):
    message = ''
    def setMessage(self, msg):
        self.message = msg

def main():
    f = TestProtocolFactory()
    f.setMessage("my message")
    reactor.connectTCP(...)
    reactor.run()

我真正想做的是通过reactor调用self.transport.write(…)(实际上,从另一个执行线程按需调用TestProtocolFactory::setMessage()),而不仅仅是在建立连接时

视情况而定。以下是一些可能性:

我想

方法1。您有一个要发送给服务器的命令列表,但由于某些原因,无法同时执行所有命令。在这种情况下,在上一个答案返回时发送一个新答案:

class proto(parentProtocol):
    def stringReceived(self, data):
        self.handle_server_response(data)
        next_command = self.command_queue.pop()
        # do stuff
方法2。您向服务器发送的内容取决于服务器向您发送的内容:

class proto(parentProtocol):
    def stringReceived(self, data):
        if data == "this":
            self.sendString("that")
        elif data == "foo":
            self.sendString("bar")
        # and so on
方法3。您不关心服务器发送到什么,只想定期发送一些命令:

class proto(parentProtocol):
    def callback(self):
        next_command = self.command_queue.pop()
        # do stuff
    def connectionMade(self):
        from twisted.internet import task
        self.task_id = task.LoopingCall(self.callback)
        self.task_id.start(1.0)
方法4:您的编辑现在提到从另一个线程触发。请随意查看twisted文档,以了解
proto.sendString
是否是线程安全的。你可以直接打电话,但我不知道。不过,方法3是线程安全的。只需从另一个线程填充队列(这是线程安全的)

基本上,您可以在协议中存储任意数量的状态;它将一直存在,直到你完成。您可以向服务器发送命令作为对其发送给您的消息的响应,或者设置一些日程安排来完成您的工作。或者两者兼有。

您可能需要使用一个

服务是Twisted应用程序中的一部分功能,可以启动和停止,是代码其他部分可以交互的很好的抽象。例如,在本例中,您可能会有一个SayStuffToServerService(我知道,这个名字很糟糕,但我不知道它的工作,在这里我只能做到这点:)暴露了如下内容:

class SayStuffToServerService:
    def __init__(self, host, port):
        # this is the host and port to connect to

    def sendToServer(self, whatToSend):
        # send some line to the remote server

    def startService(self):
        # call me before using the service. starts outgoing connection efforts.

    def stopService(self):
        # clean reactor shutdowns should call this method. stops outgoing
        # connection efforts.
(这可能就是您所需要的所有接口,但应该非常清楚您可以在何处添加内容。)

这里的
startService()
stopService()
方法正是Twisted服务公开的内容。另外,还有一个预制的Twisted服务,它的作用类似于TCP客户端,为您处理所有反应器的事务。它是twisted.application.internet.TCPClient,它接受远程主机和端口的参数,以及处理实际连接尝试的ProtocolFactory

下面是SayStuffToServerService,它是作为
TCPClient
的子类实现的:

from twisted.application import internet

class SayStuffToServerService(internet.TCPClient):
    factoryclass = SayStuffToServerProtocolFactory

    def __init__(self, host, port):
        self.factory = self.factoryclass()
        internet.TCPClient.__init__(self, host, port, self.factory)

    def sendToServer(self, whatToSend):
        # we'll do stuff here
(请参见下面的SayStuffToServerProtocolFactory。)

使用这种服务架构在很多方面都很方便;您可以将服务组合在一个容器中,这样当您的应用程序中有不同的部分需要激活时,它们都可以作为一个整体停止和启动。将应用程序的其他部分作为单独的服务来实现可能很有意义。您可以将服务设置为
应用程序的子服务
——这是
twistd
查找的神奇名称,以便了解如何初始化、监控和关闭应用程序。实际上是的,现在让我们添加一些代码来实现这一点

from twisted.application import service

...

application = service.Application('say-stuff')

sttss = SayStuffToServerService('localhost', 65432)
sttss.setServiceParent(service.IServiceCollection(application))
就这些。现在,当您在
twistd
下运行此模块时(即,为了进行调试,
twistd-noy saystuff.py
),该
应用程序将在正确的反应器下启动,它将依次启动saystuff-toserver服务,该服务将启动到localhost:65432的连接工作,它将使用服务的
工厂
属性来设置连接和协议。您不再需要调用
reactor.run()
或自己将东西附加到reactor

所以我们还没有实现SayStuffToServerProtocolFactory。由于听起来您更希望在客户端失去连接时重新连接(这样,
sendToServer
的调用者通常可以假定存在工作连接),因此我将把这个协议工厂放在
重新连接客户端工厂
之上

from twisted.internet import protocol

class SayStuffToServerProtocolFactory(protocol.ReconnectingClientFactory):
    _my_live_proto = None
    protocol = SayStuffToServerProtocol
这是一个非常好的最小定义,它将继续尝试与我们指定的主机和端口建立传出TCP连接,并每次实例化一个SaySuffToServerProtocol。当我们连接失败时,这个类将进行良好的、性能良好的指数退避,这样您的网络就不会受到影响(您可以设置最大等待时间)。协议将负责分配给
\u my\u live\u proto
并调用此工厂的
resetDelay()
方法,以便指数退避将继续按预期工作。这是现在的协议:

class SayStuffToServerProtocol(basic.LineReceiver):
    def connectionMade(self):
        # if there are things you need to do on connecting to ensure the
        # connection is "all right" (maybe authenticate?) then do that
        # before calling:
        self.factory.resetDelay()
        self.factory._my_live_proto = self

    def connectionLost(self, reason):
        self.factory._my_live_proto = None
        del self.factory

    def sayStuff(self, stuff):
        self.sendLine(stuff)

    def lineReceived(self, line):
        # do whatever you want to do with incoming lines. often it makes sense
        # to have a queue of Deferreds on a protocol instance like this, and
        # each incoming response gets sent to the next queued Deferred (which
        # may have been pushed on the queue after sending some outgoing
        # message in sayStuff(), or whatever).
        pass
这是在twisted.protocols.basic.LineReceiver
之上实现的,但在协议不面向行的情况下,它也适用于任何其他类型的协议

剩下的就是将服务连接到正确的协议实例。这就是工厂保留
\u my\u live\u proto
属性的原因,该属性应在成功建立连接时设置,并在连接丢失时清除(设置为无)。下面是
saytufftoserverservice.sendToServer的新实现:

class NotConnectedError(Exception):
    pass

class SayStuffToServerService(internet.TCPClient):

    ...

    def sendToServer(self, whatToSend):
        if self.factory._my_live_proto is None:
            # define here whatever behavior is appropriate when there is no
            # current connection (in case the client can't connect or
            # reconnect)
            raise NotConnectedError
        self.factory._my_live_proto.sayStuff(whatToSend)
现在把一切都联系起来
from twisted.application import internet, service
from twisted.internet import protocol
from twisted.protocols import basic

class SayStuffToServerProtocol(basic.LineReceiver):
    def connectionMade(self):
        # if there are things you need to do on connecting to ensure the
        # connection is "all right" (maybe authenticate?) then do that
        # before calling:
        self.factory.resetDelay()
        self.factory._my_live_proto = self

    def connectionLost(self, reason):
        self.factory._my_live_proto = None
        del self.factory

    def sayStuff(self, stuff):
        self.sendLine(stuff)

    def lineReceived(self, line):
        # do whatever you want to do with incoming lines. often it makes sense
        # to have a queue of Deferreds on a protocol instance like this, and
        # each incoming response gets sent to the next queued Deferred (which
        # may have been pushed on the queue after sending some outgoing
        # message in sayStuff(), or whatever).
        pass

class SayStuffToServerProtocolFactory(protocol.ReconnectingClientFactory):
    _my_live_proto = None
    protocol = SayStuffToServerProtocol

class NotConnectedError(Exception):
    pass

class SayStuffToServerService(internet.TCPClient):
    factoryclass = SayStuffToServerProtocolFactory

    def __init__(self, host, port):
        self.factory = self.factoryclass()
        internet.TCPClient.__init__(self, host, port, self.factory)

    def sendToServer(self, whatToSend):
        if self.factory._my_live_proto is None:
            # define here whatever behavior is appropriate when there is no
            # current connection (in case the client can't connect or
            # reconnect)
            raise NotConnectedError
        self.factory._my_live_proto.sayStuff(whatToSend)

application = service.Application('say-stuff')

sttss = SayStuffToServerService('localhost', 65432)
sttss.setServiceParent(service.IServiceCollection(application))
class SyncTransport(Protocol):
    # protocol
    def dataReceived(self, data):
        print 'receive data', data
    def connectionMade(self):
        print 'i made a sync connection, wow'
        self.transport.write('x')
        self.state = I_AM_LIVING
    def connectionLost(self):
        print 'i lost my sync connection, sight'
    def send(self, data):
        if self.state == I_AM_LIVING:
            if data == 'x':
              self.transport.write('y')
           if data == 'Y':
              self.transport.write('z')
              self.state = WAITING_DEAD
        if self.state == WAITING_DEAD:
              self.transport.close()