firstworks Programming with Rudiments using the Base Classes

Programming with Rudiments using the Base Classes

- Using the Daemon Class
- Using the Server Socket Class
- Using the Client Socket Class
- Using the Complex Socket Initialization methods of the Server Class

Using the Daemon Class

Here is some sample code for a daemon process that writes "hello" every 2 seconds. Most daemons don't write anything to the console, but this one does for purposes of demonstration.

    #include <rudiments/daemonprocess.h>
    #include <iostream.h>
    #include <unistd.h>
    #include <stdlib.h>

    class mydaemon: public daemonprocess {
            public:
                    void    init();
                    void    loop();
    };

    void    mydaemon::init() {
            runAsUser("nobody");
            runAsGroup("nobody");
            detach();
    }

    void    mydaemon::loop() {
            for (;;) {
                    cout << "hello" << endl;
                    sleep(2);
            }
    }

    mydaemon        *md;

    void    shutDown() {
            delete md;
            exit(0);
    }

    main() {
            md=new mydaemon;
            md->handleShutDown((void *)shutDown);
            md->init();
            md->loop();
    }

Using the Server Socket Class

Daemons are commonly used to serve data to clients over a network. Below is an example combining the daemon and serversocketpool classes. This server receives a string from the client and writes the same string back to the client.

    #include <rudiments/daemonprocess.h>
    #include <rudiments/serversocketpool.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/stat.h>

    class myserver: public daemonprocess, public serversocketpool {
            public:
                            myserver();
                            ~myserver();
                    void    loop();
                    void    session();
            private:
                    serversocket    inetsocket;
                    serversocket    unixsocket;
                    genericsocket   *clientsocket;
    };

    myserver::myserver() : daemonprocess(), serversocketpool() {

            // run as a different user/group
            runAsUser("nobody");
            runAsGroup("nobody");

            // detach from the controlling tty
            detach();

            // open, bind and listen on an inet port and unix socket
            inetsocket.listenOnInetPort(8040,15);
            unixsocket.listenOnUnixPort("/tmp/mysocket",S_IRUSR|S_IWUSR,15);

            // add the sockets to the pool
            addSocket(&inetsocket);
            addSocket(&unixsocket);
    }

    myserver::~myserver() {
            inetsocket.closeSocket();
            unixsocket.closeSocket();
    }

    void    myserver::loop() {

            for (;;) {

                    // accept connections, with no timeout
                    serversocket    *whichsocket;
                    waitForClientConnection(-1,-1,&whichsocket,&clientsocket);

                    // fork to handle the new connection
                    if (!fork()) {

                            // close the duplicated server socket,
                            // engage in a session with the client,
                            // close the client socket and exit
                            whichsocket->stopListening();
                            session();
                            clientsocket->closeSocket();
                            _exit(0);
                    }

                    // close the client socket, but leave the server
                    // socket open to accept more connections
                    clientsocket->closeSocket();
            }
    }

    void    myserver::session() {

            // read a string from the client
            short   bytes;
            readData(&bytes);
            char    *string=new char[bytes+1];
            readData(string,bytes);

            // write the same string back to the client
            writeData(bytes);
            writeData(string,bytes);

            // clean up
            delete[] string;
    }

    myserver        *ms;

    void    shutDown() {
            delete ms;
            exit(0);
    }

    main() {
            ms=new myserver();
            ms->handleShutDown((void *)shutDown);
            ms->loop();
    }

Notice that this server listens on both inet and unix ports. Inet ports allow clients and servers to talk across a network. Unix ports allow clients and servers on the same machine to talk through a pipe. Though clients and servers on the same machine could talk over inet ports, unix ports are much faster and use fewer system resources.

Using the Client Socket Class

Here's the code for a client that can talk to the server above. This client sends a string to the server, reads what the server sends back and prints it out. It does this once over an inet port and once over a unix port.

    #include <rudiments/clientsocket.h>
    #include <iostream.h>

    class myclient: public clientsocket {
            public:
                    void    inetSession(char *host, int port, char *message);
                    void    unixSession(char *socket, char *message);
            private:
                    void    session(char *message);
    };

    void    myclient::inetSession(char *host, int port, char *message) {
            connectToServer(host,port,0,1);
            session(message);
    }

    void    myclient::unixSession(char *socket, char *message) {
            connectToServer(socket,0,1);
            session(message);
    }

    void    myclient::session(char *message) {

            // write a message to the server
            writeData((short)strlen(message));
            writeData(message);

            // read a message back from the server
            short   bytes;
            readData(&bytes);
            char    *string=new char[bytes+1];
            readData(string,bytes);

            // write out the message we got from the server
            cout << string << endl;

            // clean up
            delete[] string;
    }

    main() {

            // declare an instance of our client
            myclient        mc;

            // connect to a known inet host/port and unix socket and
            // send a message over each connection
            mc.inetSession("localhost",8040,"message over inet port");
            mc.unixSession("/tmp/mysocket","message over unix port");
    }

Using the Complex Socket Initialization methods of the Server Class

Setting up a server to listen on a socket is actually a multi-step process. The listenOnInetPort(), listenOnUnixPort() and listenOnPorts() methods simplify this process but some applications may require a more flexible interface. If you need to set socket options or perform additional actions between the steps of socket initialization, you can use the Complex Socket Inititalization methods of the server class.

Below is an alternative implementation of the myserver constructor in which some socket options are set.

    myserver::myserver() : daemonprocess(), serversocketpool() {

            // run as a different user/group
            runAsUser("nobody");
            runAsGroup("nobody");

            // detach from the controlling tty
            detach();

            // initialize the ports
            inetsocket.initInetPort(8040);
            unixsocket.initUnixPort("/tmp/mysocket",S_IRUSR|S_IWUSR,15);

            // set some socket options
            inetsocket.lingerOnClose(10);
            inetsocket.reuseAddresses();
            unixsocket.lingerOnClose(10);

            // bind to the ports
            inetsocket.bindSocket();
            unixsocket.bindSocket();

            // listen on the ports
            inetsocket.listenForClientConnection(15);
            unixsocket.listenForClientConnection(15);

            // add sockets to the pool
            addSocket(&inetsocket);
            addSocket(&unixsocket);
    }