Note that UnixDatagramServer derives from UDPServer, not
from UnixStreamServer -- the only difference between an IP and a
Unix stream server is the address family, which is simply repeated in both
Unix server classes.

Forking and threading versions of each type of server can be created using
the ForkingMixIn and ThreadingMixIn mix-in classes. For
instance, a threading UDP server class is created as follows:

class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass

The mix-in class must come first, since it overrides a method defined in
UDPServer. Setting the various member variables also changes the
behavior of the underlying server mechanism.

To implement a service, you must derive a class from
BaseRequestHandler and redefine its handle() method. You
can then run various versions of the service by combining one of the server
classes with your request handler class. The request handler class must be
different for datagram or stream services. This can be hidden by using the
handler subclasses StreamRequestHandler or DatagramRequestHandler.

Of course, you still have to use your head! For instance, it makes no sense
to use a forking server if the service contains state in memory that can be
modified by different requests, since the modifications in the child process
would never reach the initial state kept in the parent process and passed to
each child. In this case, you can use a threading server, but you will
probably have to use locks to protect the integrity of the shared data.

On the other hand, if you are building an HTTP server where all data is
stored externally (for instance, in the file system), a synchronous class
will essentially render the service "deaf" while one request is being
handled - which may be for a very long time if a client is slow to receive
all the data it has requested. Here a threading or forking server is
appropriate.

In some cases, it may be appropriate to process part of a request
synchronously, but to finish processing in a forked child depending on the
request data. This can be implemented by using a synchronous server and
doing an explicit fork in the request handler class handle()
method.

Another approach to handling multiple simultaneous requests in an
environment that supports neither threads nor fork() (or where
these are too expensive or inappropriate for the service) is to maintain an
explicit table of partially finished requests and to use select()
to decide which request to work on next (or whether to handle a new incoming
request). This is particularly important for stream services where each
client can potentially be connected for a long time (if threads or
subprocesses cannot be used).