Twisted - 如何在子进程中使用adoptStreamConnection后告诉reactor处理Protocol对象?

时间:2016-01-09 16:04:46

标签: python tcp twisted

我尝试使用adoptStreamConnection将TCP连接传递给Twisted子进程,但我无法弄清楚如何在执行此操作后将进程置于主进程中。

我想要的流程如下:

  • 完成编写协议传输等待的任何数据
  • 当我们知道写缓冲区为空时,发送AMP消息以将套接字传输到子进程
  • 在主进程中配置协议实例

我尝试了什么,lostConnection,abortConnection和猴子修补_socketClose并使用loseConnection。请参阅此处的代码:

import weakref
from twisted.internet import reactor
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.python.sendmsg import getsockfam
from twisted.internet.protocol import Factory, Protocol
import twisted.internet.abstract

class EchoProtocol(Protocol):
    def dataReceived(self, data):
        self.transport.write(data)

class EchoFactory(Factory):
    protocol = EchoProtocol

class TransferProtocol(Protocol):
    def dataReceived(self, data):
        self.transport.write('main process still listening!: %s' % (data))

    def connectionMade(self):
        self.transport.write('this message should make it to the subprocess\n')

        # attempt 1: do nothing
        #    everything works fine in the adopt (including receiving the written message), but old protocol still exists (though isn't doing anything)

        # attempt 1: try calling loseConnection
        #    we lose connection before the adopt opens the socket (presumably TCP disconnect message was sent)
        #
        # self.transport.loseConnection()

        # attempt 2: try calling abortConnection
        #    result is same as loseConnection
        #
        # self.transport.abortConnection()

        # attempt 3: try monkey patching the socket close out and calling loseConnection
        #    result: same as doing nothing-- adopt works (including receiving the written message), old protocol still exists
        #
        # def ignored(*args, **kwargs):
        #       print 'ignored :D'
        #
        # self.transport._closeSocket = ignored
        # self.transport.loseConnection()

        reactor.callLater(0, adopt, self.transport.fileno())

class ServerFactory(Factory):
    def buildProtocol(self, addr):
        p = TransferProtocol()
        self.ref = weakref.ref(p)
        return p

f = ServerFactory()

def adopt(fileno):
    print "does old protocol still exist?: %r" % (f.ref())
    reactor.adoptStreamConnection(fileno, getsockfam(fileno), EchoFactory())

port = 1337
endpoint = TCP4ServerEndpoint(reactor, port)
d = endpoint.listen(f)
reactor.run()

在所有情况下,在传输套接字后,Protocol对象仍然存在于主进程中。我该如何清理它?

提前致谢。

1 个答案:

答案 0 :(得分:2)

loseConnectionabortConnection都没有告诉反应堆“忘记”连接;他们关闭连接,这是非常不同的;他们告诉 peer 连接已经消失。

您想要致电self.transport.stopReading()self.transport.stopWriting(),以便从反应堆中移除对它的引用。

此外,除非先调用gc.collect(),否则使用weakref测试对象的剩余存在是无效的。

确保已发送所有数据:唯一可靠的方法是对您发送的数据进行应用程序级确认。这就是需要涉及更改协议的握手的协议 - 例如,STARTTLS - 具有特定的握手,其中发起者说“我要切换”(然后停止发送),然后是对等说“好的,你现在可以切换”。在这种情况下处理这种情况的另一种方法是通过其他渠道将您想要写入的数据传递给子流程,而不是将其传递给transport.write