Writing Clients

Overview

Twisted is a framework designed to be very flexible, and let you write
powerful clients. The cost of this flexibility is a few layers in the way
to writing your client. This document covers creating clients that can be
used for TCP, SSL and Unix sockets. UDP is covered in
a different document.

At the base, the place where you actually implement the protocol parsing
and handling, is the Protocol class. This class will usually be
descended
from twisted.internet.protocol.Protocol. Most
protocol handlers inherit either from this class or from one of its
convenience children. An instance of the protocol class will be instantiated
when you connect to the server and will go away when the connection is
finished. This means that persistent configuration is not saved in the
Protocol.

The persistent configuration is kept in a Factory
class, which usually inherits from twisted.internet.protocol.Factory
(or twisted.internet.protocol.ClientFactory: see
below). The default factory class just instantiates the Protocol
and then sets the protocol's factory attribute to point to
itself (the factory). This lets the Protocol access, and
possibly modify, the persistent configuration.

Protocol

As mentioned above, this and auxiliary classes and functions are where
most of the code is. A Twisted protocol handles data in an asynchronous
manner. This means that the protocol never waits for an event, but rather
responds to events as they arrive from the network.

This is one of the simplest protocols. It just writes whatever it reads
from the connection to standard output. There are many events it does not
respond to. Here is an example of a Protocol responding to
another event:

This protocol connects to the server, sends it a welcome message, and
then terminates the connection.

The connectionMade event is
usually where set up of the Protocol object happens, as well as
any initial greetings (as in the
WelcomeMessage protocol above). Any tearing down of
Protocol-specific objects is done in connectionLost.

Simple, single-use clients

In many cases, the protocol only needs to connect to the server once, and
the code just wants to get a connected instance of the protocol. In those
cases twisted.internet.endpoints provides the
appropriate API.

Regardless of the type of client endpoint, the way to set up a new
connection is simply to call the connect method on it and pass
in a factory. This means it's easy to change the mechanism you're using to
connect, without changing the rest of your program. For example, to run
the greeter example over SSL, the only change required is to instantiate an
SSL4ClientEndpoint instead of a
TCP4ClientEndpoint. To take advantage of this, functions and
methods which initiates a new connection should generally accept an
endpoint as an argument and let the caller construct it, rather than taking
arguments like 'host' and 'port' and constructing its own before calling
connect.

Note: If you've used ClientFactory before,
make sure you remember that the connect method takes a
Factory, not a ClientFactory. Even if you pass a
ClientFactory to endpoint.connect, its
clientConnectionFailed and clientConnectionLost
methods.

You may come across code using ClientCreator, an older API which is not as flexible as
the endpoint API. Rather than calling connect on an endpoint,
such code will look like this:

In general, the endpoint API should be preferred in new code, as it lets
the caller select the method of connecting.

ClientFactory

Still, there's plenty of code out there that uses lower-level APIs, and
a few features (such as automatic reconnection) have not been
re-implemented with endpoints yet, so in some cases they may be more
convenient to use.

To use the lower-level connection APIs, you will need to call one of the
reactor.connect* methods directly. For these cases, you need a
ClientFactory.
The ClientFactory is in charge of creating the
Protocol and also receives events relating to the connection
state. This allows it to do things like reconnect in the event of a
connection error. Here is an example of a simple ClientFactory
that uses the Echo protocol (above) and also prints what state
the connection is in.

The connector passed as the first argument is the interface between a
connection and a protocol. When the connection fails and the factory
receives the clientConnectionLost event, the factory can
call connector.connect() to start the connection over again
from scratch.

However, most programs that want this functionality should
implement ReconnectingClientFactory instead,
which tries to reconnect if a connection is lost or fails and which
exponentially delays repeated reconnect attempts.

Here is the Echo protocol implemented with
a ReconnectingClientFactory:

ircLogBot.py connects to an IRC server, joins a channel, and
logs all traffic on it to a file. It demonstrates some of the
connection-level logic of reconnecting on a lost connection, as well as
storing persistent data in the Factory.

Persistent Data in the Factory

Since the Protocol instance is recreated each time the
connection is made, the client needs some way to keep track of data that
should be persisted. In the case of the logging bot, it needs to know which
channel it is logging, and where to log it.

When the protocol is created, it gets a reference to the factory as
self.factory. It can then access attributes of the factory in
its logic. In the case of LogBot, it opens the file and
connects to the channel stored in the factory.

Further Reading

The Protocol
class used throughout this document is a base implementation
of IProtocol
used in most Twisted applications for convenience. To learn about the
complete IProtocol interface, see the API documentation for
IProtocol.

The transport attribute used in some examples in this
document provides the ITCPTransport interface. To learn
about the complete interface, see the API documentation
for ITCPTransport.

Interface classes are a way of specifying what methods and attributes an
object has and how they behave. See the
Components: Interfaces and Adapters document for more information on
using interfaces in Twisted.