August'24: Kamaelia is in maintenance mode and will recieve periodic updates, about twice a year, primarily targeted around Python 3 and ecosystem compatibility. PRs are always welcome. Latest Release: 1.14.32 (2024/3/24)

Kamaelia.Chassis.ConnectedServer

Connected Servers

These 'chassis' style components are used to implementing connected servers. The most common example of this is a server which runs on top of the TCP. Examples include: a web server, email server, imap server, game protocol server, etc.

At present, there are two variants of this: ServerCore and SimpleServer (You are generally recommended to use ServerCore)

Both of these revolve around building TCP based servers. They handle the mechanics of creating the listening component, and when new connections come in, creating instances of your protocol handler components to handle the connections.

As a result, the primary arguments are the port to listen on and a function call or class name that when called returns a component for handling this connection.

Your protocol handler then receives data from a specific client on its inbox "inbox" and sends data to that same client on its outbox "outbox".

ServerCore passes additional information about the connection to the function that creates the protocol handler. You are not required to do anything with that information if you don't need to.

Aside from that, ServerCore & SimpleServer are used in the same way. (ServerCore is just an extension, and rationalisation of the older simple server code).

There is more information here: http://www.kamaelia.org/Cookbook/TCPSystems

Example Usage

A server using a simple echo protocol, that just echoes back anything sent by the client. Becase the protocol has no need to know any details of the connection, the SimpleServer component is used:

import Axon
from Kamaelia.Chassis.ConnectedServer import SimpleServer

PORTNUMBER = 12345
class EchoProtocol(Axon.Component.component):

    def main(self):
        while not self.shutdown():
            yield 1
            if self.dataReady("inbox"):
                data = self.recv("inbox")
                self.send(data, "outbox")

    def shutdown(self):
        if self.dataReady("control"):
            msg = self.recv("control")
            return isinstance(msg, Axon.Ipc.producerFinished)

simpleServer = SimpleServer( protocol = EchoProtocol, port = PORTNUMBER )
simpleServer.run()

Try connecting to this server using the telnet command, and it will echo back to you every character you type.

A more complex server might need to inform the protocol of the IP address and port of the client that connects, or the ip address and port at this (the server end) to which the client has connected. For this, ServerCore is used:

import Axon
from Axon.Ipc import shutdownMicroprocess
from Kamaelia.Chassis.ConnectedServer import ServerCore

PORTNUMBER = 12345
class CleverEchoProtocol(Axon.Component.component):

    def main(self):
        welcomeMessage =                 "Welcome! You have connected to %s on port %d from %s on port %d" %                 (self.localip, self.localport, self.peer, self.peerport)

        self.send(welcomeMessage, "outbox")
        while not self.shutdown():
            yield 1
            if self.dataReady("inbox"):
                data = self.recv("inbox")
                self.send(data, "outbox")

    def shutdown(self):
        if self.dataReady("control"):
            msg = self.recv("control")
            return isinstance(msg, Axon.Ipc.producerFinished)

myServer = ServerCore( protocol = CleverEchoProtocol, port = PORTNUMBER )
myServer.run()

Example output when telnetting to this more complex server, assuming both server and telnet session are running on the same host, and the server is listening to port number 8081:

$ telnet localhost 8081
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Welcome! You have connected to 127.0.0.1 on port 8081 from 127.0.0.1 on port 47316

Why is this useful?

Provides a framework for creating generic protocol handlers to deal with information coming in on a single port (and a single port only). This however covers a large array of server types.

A protocol handler is simply a component that can receive and send data (as byte strings) in a particular format and with a particular behaviour - ie. conforming to a particular protocol.

Provide this chassis with a factory function to create a component to handle the protocol. Whenever a client connects a handler component will then be created to handle communications with that client.

Data received from the client will be sent to the protocol handler component's "inbox" inbox. To send data back to the client, the protocol handler component should send it out of its "outbox" outbox.

For the SingleServer component, the factory function takes no arguments. It should simply return the component that will be used to handle the protocol, for example:

def makeNewProtocolHandler():
    return MyProtocolComponent()

For the ServerCore component, the factory function must accept the following arguments (with these names):

  • peer -- the address of the remote endpoint (the client's address)
  • peerport -- the port number of the remote endpoint (the port number from which the client connection originated)
  • localip -- the address of the local endpoint (this end of the connection)
  • localport -- the port number of the local endpoint (this end of the connection)

For example:

def makeNewProtocolHandler(peer, peerport, localip, localport):
    print "Debugging: client at address "+peer+" on port "+str(peerport)
    print " ... has connected to address "+localip+" on port "+str(localport)
    return MyProtocolComponent()

Do not activate the component. SingleServer or ServerCore will do this once the component is wired up.

Writing a protocol handler

A protocol handler component should use its standard inboxes ("inbox" and "control") and outboxes ("outbox" and "signal") to communicate with client it is connected to.

  • Bytes received from the client will be sent to the "inbox" inbox as a string.
  • Send a string out of the "outbox" outbox to send bytes back to the client.

If the connection is closed, a Kamaelia.IPC.socketShutdown message will arrive at the protocol handler's "control" inbox. If this happens then the connection should be assumed to have already closed. Any more messages sent will not be sent to the client. The protocol handler should react by terminating as soon as possible.

To cause the connection to close, send a producerFinished or shutdownMicroprocess message out of the protocol handler's "signal" outbox. As soon as this has been done, it can be assumed that the connection will be closed as soon as is practical. The protocol handler will probably also want to terminate at this point.

How does it work?

SimpleServer is based on ServerCore. It simply contains a wrapper around the protocol handler function that throws away the connection information instead of passing it in as arguments.

At initialisation the component registers a TCPServer component to listen for new connections on the specified port.

You supply a factory function that takes no arguments and returns a new protocol handler component.

When it receives a 'newCSA' message from the TCPServer (via the "_socketactivity" inbox), the factory function is called to create a new protocol handler. The protocol handler's "inbox" inbox and "outbox" outbox are wired to the ConnectedSocketAdapter (CSA) component handling that socket connection, so it can receive and send data.

If a 'shutdownCSA' message is received (via "_socketactivity") then a Kamaelia.IPC.socketShutdown message is sent to the protocol handler's "control" inbox, and both it and the CSA are unwired.

This component does not terminate. It ignores any messages sent to its "control" inbox.

In practice, this component provides no external connectors for your use.

History

This code is based on the code used for testing the Internet Connection abstraction layer.

To do

This component currently lacks an inbox and corresponding code to allow it to be shut down (in a controlled fashion). Needs a "control" inbox that responds to shutdownMicroprocess messages.


Kamaelia.Chassis.ConnectedServer.ServerCore

class ServerCore(Axon.AdaptiveCommsComponent.AdaptiveCommsComponent)

ServerCore(protocol[,port]) -> new Simple protocol server component

A simple single port, multiple connection server, that instantiates a protocol handler component to handle each connection. The function that creates the protocol must access arguments providing information about the connection.

Keyword arguments:

  • protocol -- function that returns a protocol handler component
  • port -- Port number to listen on for connections (default=1601)

Inboxes

  • control : We expect to get serverShutdown messages here
  • _socketactivity : Messages about new and closing connections here

Outboxes

  • _serversignal : we send shutdown messages to the TCP server here

Methods defined here

Warning!

You should be using the inbox/outbox interface, not these methods (except construction). This documentation is designed as a roadmap as to their functionalilty for maintainers and new component developers.

__init__(self, **argd)

x.__init__(...) initializes x; see x.__class__.__doc__ for signature

handleClosedCSA(self, shutdownCSAMessage)

handleClosedCSA(shutdownCSAMessage) -> None

Terminates and unwires the protocol handler for the closing socket.

Keyword arguments: shutdownCSAMessage -- shutdownCSAMessage.object is the ConnectedSocketAdapter for socket that is closing.

handleNewConnection(self, newCSAMessage)

handleNewConnection(newCSAMessage) -> Axon.Ipc.newComponent(protocol handler)

Creates and returns a protocol handler for new connection.

Keyword arguments:

  • newCSAMessage -- newCSAMessage.object is the ConnectedSocketAdapter component for the connection

initialiseServerSocket(self)

main(self)

mkProtocolHandler(self, **sock_info)

stop(self)

Kamaelia.Chassis.ConnectedServer.SimpleServer

class SimpleServer(ServerCore)

SimpleServer(protocol[,port]) -> new Simple protocol server component

A simple single port, multiple connection server, that instantiates a protocol handler component to handle each connection.

Keyword arguments:

  • protocol -- function that returns a protocol handler component
  • port -- Port number to listen on for connections (default=1601)

Inboxes

Outboxes

Methods defined here

Warning!

You should be using the inbox/outbox interface, not these methods (except construction). This documentation is designed as a roadmap as to their functionalilty for maintainers and new component developers.

__init__(self, **argd)

mkProtocolHandler(self, **sock_info)

Methods inherited from Kamaelia.Chassis.ConnectedServer.MoreComplexServer :

Feedback

Got a problem with the documentation? Something unclear that could be clearer? Want to help improve it? Constructive criticism is very welcome - especially if you can suggest a better rewording!

Please leave you feedback here in reply to the documentation thread in the Kamaelia blog.

-- Automatic documentation generator, 05 Jun 2009 at 03:01:38 UTC/GMT