File descriptor listeners and servers

#include <x/fdlistener.H>

class myServerThread : virtual public x::obj {

public:
    void run(const x::fd &raw_socket,
             const x::fd &termfd)
    {
        raw_socket->nonblock(true);

        x::fdtimeout socket(x::fdtimeout::create(raw_socket));

        socket->set_terminate_fd(termfd);

        // read and write from socket.
    }
};

x::ref<myServerThread> server(x::ref<myServerThread>::create());

x::fdlistener listener(x::fdlistener::create(4000));

listener->start(server);

// ...

listener->stop();
listener->wait();

x::fdlistener is a reference to a reference-counted object that implements a thread which listens on one or more sockets for incoming connections, starting a new thread for each established connection.

create() creates a new listener object. Its parameter may be one of the following:

A port number

A listening SOCK_STREAM socket is created, bound to this port, and listen(3)d on.

A string containing a list of port names or numbers

A listening SOCK_STREAM socket is created for each port in the given list, separated by commas or spaces. Each file descriptor is bound to the port and listen(3)d on.

A std::list<int>

A listening SOCK_STREAM socket is created for multiple port numbers specified by the given list. Each socket is bound to a port and listen(3)d on.

A std::list<x::fd>

This is presumed to be a list of file descriptors that have already been bound to their appropriate ports. Each file descriptor in the list is listen(3)d on.

create() also takes several optional parameters for its internal thread worker, which are described in the worker pool documentation.

The constructor instantiates an object. start() spawns a thread that accepts connections from listening sockets, and starts other threads, for each accepted connection. The parameter to start() is a reference to an object that defines a run() method, as given in the example above. run() gets invoked for each accepted connection, in its own separate thread, and all connection threads use the same server object's run() method.

The first parameter to run() is a reference to a file descriptor object for the new socket. The second parameter is a reference to a terminator file descriptor. This file descriptor becomes readable if the connection thread is still running when the listener thread stops.

The connection thread should terminate when the terminator file descriptor becomes readable. The above example demonstrates the direct approach of directly setting it as a file descriptor terminator for the raw socket.

The run() method may also take additional parameters that get forwarded by the listener object's start():

#include <x/fdlistener.H>

class documentObj;

typedef x::ref<documentObj> document;


class myServerThread : virtual public x::obj {

public:
    void run(const x::fd &raw_socket,
             const x::fd &termfd,
             const document &doc)
    {
        raw_socket->nonblock(true);

        x::fdtimeout socket(x::fdtimeout::create(raw_socket));

        socket->set_terminate_fd(termfd);

        // read and write from socket.
    }
};

x::ref<myServerThread> server(x::ref<myServerThread>::create());

x::fdlistener listener(x::fdlistener::create(4000));

listener->start(server, document::create());

The additional parameters to the listener object's start() get forwarded as additional parameters to the server object's run().

Note

The same parameters given to start() get forwarded to each thread that gets started for each accepted connection. The parameters given to start() get saved. The parameters must be copy-constructible. Each started thread is passed a copy of the same saved parameter values.

Only one copy of the stored parameters gets saved. Each thread gets a reference to a constant parameter value. The thread's run() must declare the forwarded parameter types as rvalues or as references to constant types.