This module provides the basic infrastructure for writing asynchronous socket service clients and servers.
There are only two ways to have a program on a single processor do ``more than one thing at a time.'' Multi-threaded programming is the simplest and most popular way to do it, but there is another very different technique, that lets you have nearly all the advantages of multi-threading, without actually using multiple threads. It's really only practical if your program is largely I/O bound. If your program is CPU bound, then pre-emptive scheduled threads are probably what you really need. Network servers are rarely CPU-bound, however.
If your operating system supports the select() system call in its I/O library (and nearly all do), then you can use it to juggle multiple communication channels at once; doing other work while your I/O is taking place in the ``background.'' Although this strategy can seem strange and complex, especially at first, it is in many ways easier to understand and control than multi-threaded programming. The module documented here solves many of the difficult problems for you, making the task of building sophisticated high-performance network servers and clients a snap.
The direct interface between the select loop and the socket object are the handle_read_event() and handle_write_event() methods. These are called whenever an object `fires' that event.
The firing of these low-level events can tell us whether certain higher-level events have taken place, depending on the timing and the state of the connection. For example, if we have asked for a socket to connect to another host, we know that the connection has been made when the socket fires a write event (at this point you know that you may write to it with the expectation of success). The implied higher-level events are:
Event | Description |
---|---|
handle_connect() |
Implied by a write event |
handle_close() |
Implied by a read event with no data available |
handle_accept() |
Implied by a read event on a listening socket |
This set of user-level events is larger than the basics. The full set of methods that can be overridden in your subclass are:
def handle_write(self): sent = self.send(self.buffer) self.buffer = self.buffer[sent:]
1
,
indicating that by default, all channels will be interested.
1
,
indicating that by default, all channels will be interested.
In addition, there are the basic methods needed to construct and manipulate ``channels,'' which are what we will call the socket connections in this context. Note that most of these are nearly identical to their socket partners.
(conn, address)
where conn is a
new socket object usable to send and receive data on
the connection, and address is the address bound to the
socket on the other end of the connection.