Prev Up Next

# Listeners and Clients

Usually message passing between processes is done using queues or by using connection objects returned by `Pipe()`.

However, the `processing.connection` module allows some extra flexibility. It basically gives a high level API for dealing with sockets or Windows named pipes, and also has support for digest authentication using the `hmac` module from the standard library.

# Classes and functions

The module defines the following functions:

> `Listener(address=None, family=None, backlog=1, authenticate=False, authkey=None)`
>   Returns a wrapper for a bound socket or Windows named pipe which is 'listening' for connections.
> `Client(address, family=None, authenticate=False, authkey=None)`

>   Attempts to set up a connection to the listener which is using address `address`, returning a connection object.

>   The type of the connection is determined by `family` argument, but this can generally be omitted since it can usually be inferred from the format of `address`.

>   If `authentication` or `authkey` is a string then digest authentication is used. The key used for authentication will be either `authkey` or `currentProcess.getAuthKey()` if `authkey` is `None`. If authentication fails then `AuthenticationError` is raised. See Authentication keys.

The module exports two exception types:

> exception `AuthenticationError`
>   Exception raised when there is an authentication error.
> exception `BufferTooShort`

>   Exception raise by the `recvBytesInto()` method of a connection object when the supplied buffer object is too small for the message read.

>   If `e` is an instance of `BufferTooShort` then `e.args[0]` will give the message as a byte string.

# Listener objects

Instances of `Listener` have the following methods:

> `__init__(address=None, family=None, backlog=1, authenticate=False, authkey=None)`
>   `address`
>     The address to be used by the bound socket or named pipe of the listener object.
>   `family`

>     The type of the socket (or named pipe) to use.

>     This can be one of the strings `'AF_INET'` (for a TCP socket), `'AF_UNIX'` (for a Unix domain socket) or `'AF_PIPE'` (for a Windows named pipe). Of these only the first is guaranteed to be available.

>     If `family` is `None` than the family is inferred from the format of `address`. If `address` is also `None` then a default is chosen. This default is the family which is assumed to be the fastest available. See Address formats.

>     Note that if `family` is `'AF_UNIX'` then the associated file will have only be readable/writable by the user running the current process -- use `os.chmod()` is you need to let other users access the socket.

>   `backlog`
>     If the listener object uses a socket then `backlog` is passed to the `listen()` method of the socket once it has been bound.
>   `authenticate`
>     If `authenticate` is true or `authkey` is not `None` then digest authentication is used.
>   `authkey`

>     If `authkey` is a string then it will be used as the authentication key; otherwise it must be `None`.

>     If `authkey` is `None` and `authenticate` is true then `currentProcess.getAuthKey()` is used as the authentication key.

>     If `authkey` is `None` and `authentication` is false then no authentication is done.

>     If authentication fails then `AuthenticationError` is raised. See Authentication keys.

> `accept()`

>   Accept a connection on the bound socket or named pipe of the listener object. If authentication is attempted and fails then `AuthenticationError` is raised.

>   Returns a `connection object <connection-object.html>` object.

> `close()`

>   Close the bound socket or named pipe of the listener object.

>   This is called automatically when the listener is garbage collected. However it is advisable to call it explicitly.

Listener objects have the following read-only properties:

> `address`
>   The address which is being used by the listener object.
> `last_accepted`

>   The address from which the last accepted connection came.

>   If this is unavailable then `None` is returned.

# Address formats

- An `'AF_INET'` address is a tuple of the form `(hostname, port)` where `hostname` is a string and `port` is an integer

- An `'AF_UNIX'` address is a string representing a filename on the filesystem.

- An `'AF_PIPE'` address is a string of the form `r'\\.\pipe\PipeName'`.

  To use `Client` to connect to a named pipe on a remote computer called `ServerName` one should use an address of the form `r'\\ServerName\pipe\PipeName'` instead.

Note that any string beginning with two backslashes is assumed by default to be an `'AF_PIPE'` address rather than an `'AF_UNIX'` address.

# Authentication keys

When one uses the `recv()` method of a connection object, the data received is automatically unpickled. Unfortunately unpickling data from an untrusted source is a security risk. Therefore `Listener` and `Client` use the `hmac` module to provide digest authentication.

An authentication key is a string which can be thought of as a password: once a connection is established both ends will demand proof that the other knows the authentication key. (Demonstrating that both ends are using the same key does not involve sending the key over the connection.)

If authentication is requested but do authentication key is specified then the return value of `currentProcess().getAuthKey()` is used (see Process objects). This value will automatically inherited by any `Process` object that the current process creates. This means that (by default) all processes of a multi-process program will share a single authentication key which can be used when setting up connections between the themselves.

Suitable authentication keys can also be generated by using `os.urandom()`.

# Example

The following server code creates a listener which uses `'secret password'` as an authentication key. It then waits for a connection and sends some data to the client:

    from processing.connection import Listener
    from array import array

    address = ('localhost', 6000)     # family is deduced to be 'AF_INET'
    listener = Listener(address, authkey='secret password')

    conn = listener.accept()
    print 'connection accepted from', listener.last_accepted

    conn.send([2.25, None, 'junk', float])

    conn.sendBytes('hello')

    conn.sendBytes(array('i', [42, 1729]))

    conn.close()
    listener.close()

The following code connects to the server and receives some data from the server:

    from processing.connection import Client
    from array import array

    address = ('localhost', 6000)
    conn = Client(address, authkey='secret password')

    print conn.recv()                 # => [2.25, None, 'junk', float]

    print conn.recvBytes()            # => 'hello'

    arr = array('i', [0, 0, 0, 0, 0])
    print conn.recvBytesInto(arr)    # => 8
    print arr                         # => array('i', [42, 1729, 0, 0, 0])

    conn.close()