Пример #1
0
class OnlyOnce(object):
    def __init__(self):
        self.tcp_server = TCPServer()
        try:
            self.tcp_server.listen(44556)
        except socket.error:
            raise OnlyOnceException()

    def __del__(self):
        if self.tcp_server is not None:
            self.tcp_server.stop()
Пример #2
0
 def test_stop_twice(self):
     sock, port = bind_unused_port()
     server = TCPServer()
     server.add_socket(sock)
     server.stop()
     server.stop()
Пример #3
0
 def test_stop_twice(self):
     sock, port = bind_unused_port()
     server = TCPServer()
     server.add_socket(sock)
     server.stop()
     server.stop()
Пример #4
0
class Service(object):
    """A TCP/IP listening service that responds to `denim.protocol.Msg`s.
    """
    def __init__(self, msg_cb, port=None, host='localhost', procs=1):
        """Creates a new service that calls `msg_cb` with each new request
        received. If provided, the service will listen on `host`:`port`.
        Otherwise, an OS-provided port will be used. Optional parameter
        `procs` starts multiple processes.
        """
        self.msg_cb = msg_cb
        self.port = port
        self.host = host
        self.procs = procs
        self.clients = {}
        self.pending = {}
        self.server = None

    @property
    def is_running(self):
        """Returns True if the server has been started.
        """
        return self.server is not None

    def is_pending(self, msgid):
        """Returns True if `msgid` is awaiting a response.
        """
        return msgid in self.pending

    def build_socket(self):
        """Builds the listener socket. If `self.port` is not set, uses an
        OS-assigned port. Note that the `host` and `port` values are updated
        by the `start` method.
        """
        [sock] = bind_sockets(self.port, self.host, family=socket.AF_INET)
        host, port = sock.getsockname()
        return sock, port, host

    def start(self):
        """Starts the service on the configured port and host, or uses an
        OS-assigned port if not provided.
        """
        sock, port, host = self.build_socket()
        self.port = port
        self.host = host

        self.server = TCPServer()
        self.server.handle_stream = self.handle_stream
        self.server.add_socket(sock)
        self.server.start(self.procs)

    def stop(self):
        """Stops the TCP/IP listening service.
        """
        self.server.stop()
        self.server = None

    def handle_stream(self, stream, addr):
        """Triggered by the listener when a new client connects. This method
        starts the read loop.
        """
        pipe = Pipe(stream, self.cleanup, self.on_receive)
        self.clients[pipe.fd] = pipe

    def cleanup(self, client):
        """Cleans up after a client connection.
        """
        del self.clients[client.fd]
        for msgid in self.pending.keys():
            if self.pending[msgid] == client.fd:
                # TODO trigger some sort of cancel callback so the logic
                # managing the Service can deal with a cancelled msg.
                del self.pending[msgid]

    def on_receive(self, msg, client):
        """Receives a line of data from the stream referred to by `addr`. The
        `msg_cb` callback is then called with the message object, the pipe from
        which the message was received, and a reference to the service itself.
        """
        self.pending[msg.msgid] = client.fd
        self.msg_cb(msg, client, self)

    def reply(self, msg):
        """This method is used by the caller to respond to a `Msg` that has
        been received. A `KeyError` is raised if the message id is not
        recognized.
        """
        if not self.is_pending(msg.msgid):
            raise KeyError('msgid not recognized')

        fd = self.pending[msg.msgid]
        pipe = self.clients[fd]
        pipe.send(msg)
        del self.pending[msg.msgid]

    def steal_pipe(self, pipe):
        """Allows the caller to "steal" a `Pipe` that is currently being
        managed as a client. This is particularly useful if a service wishes to
        handle certain clients differently (such as a `Manager` does when a
        `Worker` registers.)
        """
        self.cleanup(pipe)