SAI || Sommersemester 1997 || Systemnahe Software II || Übungen

<- Alle Module

Lösung zu Blatt 5 (Aufgabe 6): gbmesg.h + gbmesg.c

Kommunikation zwischen Klienten und Server (Klientenseite).

gbmesg.h

/*
 *      gbmesg.h        - headerfile for Gobang C/S communication
 *
 *      Martin Hasch, University of Ulm, June 1997
 */

#include        <unistd.h>

/*
 *      Request codes.
 */
#define RQ_SIGNOFF              0
#define RQ_SIGNON_BLACK         1
#define RQ_SIGNON_WHITE         2
#define RQ_SIGNON_KIBITZ        3
#define RQ_MOVE                 4
#define RQ_RESIGN               8
#define RQ_KILL                 15

/*
 *      Message codes.
 */
#define MSG_DRAW                0
#define MSG_BLACK_WINS          1
#define MSG_WHITE_WINS          2
#define MSG_ABORT               3
#define MSG_TOGGLE_EMPTY        4
#define MSG_TOGGLE_BLACK        5
#define MSG_TOGGLE_WHITE        6
#define MSG_SIZE                7
#define MSG_YOUR_TURN           8
#define MSG_ILLEGAL_MOVE        9

typedef struct {
        unsigned m_code;
        unsigned m_x;
        unsigned m_y;
} Message;

/*
 *      Create and try to open the communication channel to a server.
 *      Result is 1 on success, otherwise 0.
 */
int mesg_init(pid_t server);

/*
 *      Close the communication channel.
 *      Automatically called on exit.
 */
void mesg_cleanup(void);

/*
 *      Send a request.  Result is 1 on success, otherwise zero.
 */
int mesg_sendrequest(unsigned code, unsigned x, unsigned y);

/*
 *      Wait for and receive a message.  Result is 1 one success, otherwise 0.
 *      (message) must point to an allocated message buffer.
 */
int mesg_receive(Message *message);

gbmesg.c

/*
 *      gbmesg.c        - Gobang C/S communication
 *
 *      Martin Hasch, University of Ulm, June 1997
 */

#include        <sys/types.h>
#include        <sys/stat.h>
#include        <fcntl.h>
#include        <signal.h>
#include        <stdio.h>
#include        <stdlib.h>
#include        <unistd.h>
#include        "gbmesg.h"

#define SERVER_FIFO     "/tmp/gbs.%05d"
#define CLIENT_FIFO     "/tmp/gbc.%05d"
#define FIFO_NAMELEN    16

typedef unsigned long Request;
typedef unsigned short Msg;

static Request rqTemplate = 0L;         /* "constant" part of a request */
static int outputfd = -1;               /* fd of fifo we are writing to */
static int inputfd = -1;                /* fd of fifo we a reading from */
static char my_fifo[FIFO_NAMELEN];      /* node name of input fifo */
static int should_unlink = 0;           /* should my_fifo be unlinked? */

/*
 *      Map a request code and parameters to a request.
 */
#define packrq(c, x, y) ((Request) (rqTemplate | \
                ((c) & 0xf) << 12 | ((x) & 0x3f) << 6 | ((y) & 0x3f)))

/*
 *      Send a request.  Result is 1 on success, otherwise zero.
 */
int mesg_sendrequest(unsigned code, unsigned x, unsigned y)
{
        Request request;

        request = packrq(code, x, y);
        return write(outputfd, &request, sizeof request) == sizeof request;
}

/*
 *      Simple signal handling routine (for SysV):
 *      Re-installs itself and is done.
 */
static void simple_handler(int sig)
{
        (void) signal(sig, simple_handler);
}

/*
 *      Signal handler to unlink my_fifo and die.
 */
static void unlink_handler(int sig)
{
        (void) unlink(my_fifo);
        (void) kill(getpid(), sig);
}

/*
 *      Signal handler for interrupt key.  Triggers signoff and exit.
 */
static void intr_handler(int sig)
{
        if ( outputfd >= 0 )
                (void) mesg_sendrequest(RQ_SIGNOFF, 0, 0);
        exit(-1);
}

/*
 *      Close the communication channel.
 *      Automatically called on exit.
 */
void mesg_cleanup(void)
{
        if ( inputfd >= 0 )
                (void) close(inputfd), inputfd= -1;
        if ( outputfd >= 0 )
                (void) close(outputfd), outputfd= -1;
        if ( should_unlink )
                (void) unlink(my_fifo), should_unlink = 0;
}

/*
 *      If (enable != 0), prepare for my_pipe to be unlinked on termination;
 *      otherwise undo these preparations.
 *
 *      Note that old-style signal handling like this yields race conditions.
 *      For a safer approach one might want to use sigset(), sighold() etc.
 */
static void prepare_unlinking(int enable)
{
        void (* sigaction)(int);
        int sig;

        if ( enable ) {
                should_unlink = 1;
                for ( sig=SIGHUP; sig<=SIGTERM; ++sig )
                        if ( sig != SIGKILL && (sigaction = signal(sig,
                                        unlink_handler)) != SIG_DFL )
                                (void) signal(sig, sigaction);
        } else {
                should_unlink = 0;
                for ( sig=SIGHUP; sig<=SIGTERM; ++sig )
                        if ( sig != SIGKILL && (sigaction = signal(sig,
                                        SIG_DFL)) != unlink_handler )
                                (void) signal(sig, sigaction);
        }
}

/*
 *      Create and try to open the communication channel to a server.
 *      Result is 1 on success, otherwise 0.
 */
int mesg_init(pid_t server)
{
        char server_fifo[FIFO_NAMELEN];

        rqTemplate = (Request) getpid() << 16;
        (void) sprintf(server_fifo, SERVER_FIFO, (int) server);
        (void) sprintf(my_fifo, CLIENT_FIFO, (int) getpid());
        (void) signal(SIGPIPE, simple_handler);
        (void) signal(SIGINT, intr_handler);
        if ( !should_unlink ) {
                if ( mknod(my_fifo, S_IFIFO|0622, 0L) ) {
                        perror(my_fifo);
                        return 0;
                }
                prepare_unlinking(/*enable*/ 1);
        }
        if ( atexit(mesg_cleanup) ) {
                perror("atexit");
                mesg_cleanup();
                return 0;
        }
        if ( chmod(my_fifo, 0622) ) {
                perror(my_fifo);
                return 0;
        }
        if ( (outputfd = open(server_fifo, O_WRONLY)) < 0 ) {
                perror(server_fifo);
                return 0;
        }
        /*
         *      In order not to block when we open our newly created fifo
         *      we open it in no-delay mode.  This is immediately turned off,
         *      however, since it seems okay to block later on on writing.
         */
        if ( (inputfd = open(my_fifo, O_RDONLY|O_NDELAY)) < 0 ) {
                perror(my_fifo);
                (void) close(outputfd), outputfd = -1;
                return 0;
        }
        if ( fcntl(inputfd, F_SETFL, fcntl(inputfd, F_GETFL) & ~O_NDELAY) ) {
                perror(my_fifo);
                (void) close(inputfd), inputfd = -1;
                (void) close(outputfd), outputfd = -1;
                return 0;
        }
        return 1;
}

/*
 *      Wait for and receive a message.  Result is 1 one success, otherwise 0.
 *      (message) must point to an allocated message buffer.
 */
int mesg_receive(Message *message)
{
        Msg rawmsg;
        size_t bytes;

        do {
                bytes = read(inputfd, &rawmsg, sizeof rawmsg);
                if ( bytes < 0 )
                        return 0;
        } while ( bytes < sizeof rawmsg );              /* ignore rubbish */
        message->m_code = rawmsg >> 12 & 0xf;
        message->m_x = (rawmsg >> 6) & 0x3f;
        message->m_y = rawmsg & 0x3f;
        /*
         * We want to get rid of the node associated with our fifo as soon
         * as possible (i.e. once the server has plugged in), so that the
         * named pipe becomes anonymous, bound to die with the associated
         * processes.  When the first message has arrived it is therefore
         * time to say good-bye...  The signal handler cleaning up the
         * fifo on premature termination hereby becomes obsolete, too.
         */
        if ( should_unlink ) {
                (void) unlink(my_fifo);
                should_unlink = 0;
                prepare_unlinking(/*disable*/ 0);
        }
        return 1;
}
<- Alle Module
SAI || Sommersemester 1997 || Systemnahe Software II || Übungen

Martin Hasch, Juni 1997