停止反应器会杀死在不同端口上运行的所有反应器

时间:2015-10-13 19:59:34

标签: python networking twisted reactor

我试图实现这个代码,其中每个"节点"是一个独立的演员"。

from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor, defer
from twisted.protocols.basic import LineReceiver


class ChatProtocol(LineReceiver):
    def __init__(self, factory):
        self.factory = factory
        self.name = None
        self.state = "REGISTER"

    def connectionMade(self):
        self.sendLine("What's your name?")

    def connectionLost(self, reason):
        if self.name in self.factory.users:
            del self.factory.users[self.name]
            self.broadcastMessage("{} has left the channel.".format(self.name))

    def lineReceived(self, line):
        if self.state == "REGISTER":
            self.handle_REGISTER(line)
        else:
            self.handle_CHAT(line)

    def handle_REGISTER(self, name):
        if name in self.factory.users:
            self.sendLine("Name taken, please choose another!")
            return
        self.sendLine("Welcome, {}".format(name))
        self.broadcastMessage("{} has joined the channel.".format(name))
        self.name = name
        self.factory.users[name] = self
        self.state = "CHAT"

    def handle_CHAT(self, message):
        message = "[%s]>> %s" % (self.name, message)
        self.broadcastMessage(message)

    def broadcastMessage(self, message):
        for name, protocol in self.factory.users.iteritems():
            if protocol != self:
                protocol.sendLine(message)

class ChatFactory(Factory):
    """Handle all the nodes' connection"""
    def __init__(self):
        self.users = {}

    def buildProtocol(self, addr):
        return ChatProtocol(self)

class Node:
    def __init__(self, stop=None):
        self.Factory = ChatFactory
        self.reactor = reactor
        self.d = defer.Deferred()
        # with `stop` the node is bound to die
        if stop:
            self.reactor.callLater(stop, self.stop)

    def listen(self, port):
        self.reactor.listenTCP(port, self.Factory())

    def run(self):
        self.reactor.run()

    def stop(self):
        self.reactor.stop()

class Organization:
    """
    An organization consists of several nodes, with one node as a leader
    """
    def __init__(self):
        self.nodes = []

    def create_leader(self):
        # create first node now with intentionally kill the leader's reactor after 5 seconds
        leader_node = Node(5)
        leader_node.listen(8000)
        self.nodes.append(leader_node)

    def create_more_nodes(self):
        node_1 = Node()
        node_2 = Node()
        self.nodes.append(node_1)
        self.nodes.append(node_2)

    def activate(self):         
        self.nodes[1].listen(8001)
        self.nodes[2].listen(8002)

        """
        now leader_node listens at 8000
        node_1 listens at 8001
        node_2 listens at 8002
        """

        # run each node's reactor
        for n in self.nodes:
            n.run()

if __name__ == '__main__':
    org = Organization()
    org.create_leader()
    org.create_more_nodes()
    org.activate()

5秒后,leader_node的反应堆通过延迟的Node.stop()停止。但是,我不知道为什么在{800}和8002处的node_1node_2听也停止了。如果对Twisted有更多经验的人可以指出这一点,那就太棒了!

1 个答案:

答案 0 :(得分:3)

reactor.run()表示“运行整个程序”。虽然它没有强制终止(函数确实返回),但它只是为了允许你在退出之前清理一些状态。所以你应该每个进程只运行一个反应器,并在完成后立即退出。

如果您希望拥有可以关闭所有传入和传出连接以及侦听端口的自包含服务,则必须在connectionMadeconnectionLost中跟踪这些连接。您还必须跟踪您的侦听端口,以便stopListening

作为旁注,listenTCP是一个非常低级的API,您可能不应该直接调用它;相反,请使用更高级别的Endpoints API。

这是您的代码版本,它跟踪入站和连接以及侦听端口并按照您的意愿关闭它们,同时在所有节点之间共享一个反应器。

from twisted.internet.protocol import Factory
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.protocols.basic import LineReceiver
from twisted.internet.defer import Deferred

class ChatProtocol(LineReceiver):
    def __init__(self, factory):
        self.factory = factory
        self.name = None
        self.state = "REGISTER"

    def connectionMade(self):
        self.factory.node.activeTransports.append(self.transport)
        self.sendLine("What's your name?")

    def connectionLost(self, reason):
        self.factory.node.activeTransports.remove(self.transport)
        if self.name in self.factory.users:
            del self.factory.users[self.name]
            self.broadcastMessage("{} has left the channel.".format(self.name))

    def lineReceived(self, line):
        if self.state == "REGISTER":
            self.handle_REGISTER(line)
        else:
            self.handle_CHAT(line)

    def handle_REGISTER(self, name):
        if name in self.factory.users:
            self.sendLine("Name taken, please choose another!")
            return
        self.sendLine("Welcome, {}".format(name))
        self.broadcastMessage("{} has joined the channel.".format(name))
        self.name = name
        self.factory.users[name] = self
        self.state = "CHAT"

    def handle_CHAT(self, message):
        message = "[%s]>> %s" % (self.name, message)
        self.broadcastMessage(message)

    def broadcastMessage(self, message):
        for name, protocol in self.factory.users.iteritems():
            if protocol != self:
                protocol.sendLine(message)

class ChatFactory(Factory):
    """Handle all the nodes' connection"""
    def __init__(self, node):
        self.users = {}
        self.node = node

    def buildProtocol(self, addr):
        return ChatProtocol(self)

class Node:
    def __init__(self, endpoint, clock, stop=None):
        self.Factory = ChatFactory
        self._endpoint = endpoint
        self._listenStarting = None
        self._listeningPort = None
        self.activeTransports = []
        if stop is not None:
            print("Scheduling stop.", stop)
            clock.callLater(stop, self.stop)

    def listen(self):
        self._listenStarting = self._endpoint.listen(self.Factory(self))
        def setPort(port):
            self._listeningPort = port
        def clear(whatever):
            self._listenStarting = None
            return whatever
        self._listenStarting.addCallback(setPort).addBoth(clear)

    def stop(self):
        if self._listenStarting is not None:
            self._listenStarting.cancel()
        if self._listeningPort is not None:
            self._listeningPort.stopListening()
        for transport in self.activeTransports[:]:
            transport.abortConnection()

class Organization:
    def __init__(self, reactor):
        self.reactor = reactor
        self.nodes = []

    def port(self, number):
        return TCP4ServerEndpoint(self.reactor, number)

    def create_leader(self):
        leader_node = Node(self.port(8000), self.reactor, 5)
        leader_node.listen()
        self.nodes.append(leader_node)

    def create_more_nodes(self):
        node_1 = Node(self.port(8001), self.reactor)
        node_2 = Node(self.port(8002), self.reactor)
        self.nodes.append(node_1)
        self.nodes.append(node_2)

    def activate(self):
        self.nodes[1].listen()
        self.nodes[2].listen()

def main(reactor):
    org = Organization(reactor)
    org.create_leader()
    org.create_more_nodes()
    org.activate()
    return Deferred()

if __name__ == '__main__':
    from twisted.internet.task import react
    react(main)