Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Objective: Create a math server that can compute simple math numerical expressio

ID: 3786387 • Letter: O

Question

Objective: Create a math server that can compute simple math numerical expressions that is requested by a client. Requirements:

1. Create a C-based client-server architecture using sockets

3. The math server should be able to accept and service at least one client’s requests

4. The math server should be able to compute simple numerical expression with at most six non-zero integer values

5. The numerical expression could have spaces between operations and operands

6. The numerical expression cannot have multiple inner brackets or inner expression

7. The math server should be able to perform addition, subtraction, multiplication, divide, square root, power (xy), exponential (ex ), and log

8. Square root and log does not compute for negative values as input

9. The results should have at least four places of precision

10. The math server should be able to quit computation when a quit expression is given as input

11. The math server should parse the input expression and identify the operands and operations and perform the computation.

Do not use functions for expression evaluations or use any Linux shell commands or code from the internet. You could use pre-defined functions for square root, power (xy ), exponential (ex ), and log computations.

Procedure: 1. Create a C-based server that can accept at least one client’s request using sockets

2. Make sure to start the math server as follows mserver where mserver is the math server executable and port_number is the port number on which the math server listens

3. Create a C-based client that can connect to the math server using sockets

4.The user can request the numerical expression using the below format client exprsn:(5+6)*(6+5) where client is the client executable, port_number is the port number on which the client connects the server and exprsn is the requested numerical expression

5. Once the math server gets a request from the client, it computes the value of the expression and replies it to the client

6. The math server quits computation when a quit expression is given as input

7. An example client interaction with the server is shown below. Client side input/output example: client 1234 exprsn: (-5*6) + (5+5) result: -20.0000 exprsn: (10^2) + (5/5) result: 101.0000 exprsn: (e^2) + (10 - 5) result: 12.3890 exprsn: sqrt(2) + log(2) result: 1.7152 exprsn: (5*6) + (5+) result: Invalid expression exprsn: (5*6) + (-5) result: 25.0000 exprsn: sqrt(2) + log(-2) result: Invalid expression exprsn: quit result: bye bye!

Explanation / Answer

// Include the required header files

#include <sys/socket.h>

#include <sys/types.h>

#include <sys/wait.h>

#include <arpa/inet.h>

#include <unistd.h>

#include <stdio.h>

#include <stdlib.h>

#include "Wbhelper.h"

#include "wservreq.h"

#define WSERVER_PORT(80)

/* main() method */

int main(int argc, char *argv[])

{

   // Declare the required variables

    int    wblistener, wbcon;

    pid_t wbpid;

    struct sockaddr_in wbservaddr;

    /* Create the listening socket */

    if ( (wblistener = socket(AF_INET, SOCK_STREAM, 0)) < 0 )

   WbError_Quit("Not able to create listening socket.");

    /* Socket address structure generation*/

    memset(&wbservaddr, 0, sizeof(wbservaddr));

    wbservaddr.sin_family      = AF_INET;

    wbservaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    wbservaddr.sin_port        = htons(WSERVER_PORT);

    /* Assign socket address */

    if ( bind(wblistener, (struct sockaddr *) &wbservaddr, sizeof(wbservaddr)) < 0 )

   WbError_Quit("Not able to bind socket.");

    /* Create a listening socket */

    if ( listen(wblistener, LISTENQ) < 0 )

   WbError_Quit("Listen failed.");

    /* Infinite loop to accept and service connections */

    while ( 1 )

   {

   /* connection wait*/

   if ( (wbcon = accept(wblistener, NULL, NULL)) < 0 )

        WbError_Quit("Error in the calling of accept()");

   /* Fork child process */

   if ( (wbpid = fork()) == 0 ) {

        /* close listening socket and service request   */

        if ( close(wblistener) < 0 )

       WbError_Quit("Error in closing listening socket.");

        WbService_Request(wbcon);

        /* Close connected socket and exit */

        if ( close(wbcon) < 0 )

       WbError_Quit("Error in closing the connection socket.");

        exit(EXIT_SUCCESS);

   }

   /* Code to close socket, clean the processes,

        and to accept new connection. */

   if ( close(wbcon) < 0 )

        WbError_Quit("Error in closing connection in parent.");

       waitpid(-1, NULL, WNOHANG);

    }

    return EXIT_FAILURE;

wservreq.h

#ifndef WEB_SERVREQ_H

#define WEB_SERVREQ_H

/* Function prototypes */

int WbService_Request(int wbcon);

#endif /* WEB_SERVREQ_H */

wservreq.c

#include <stdio.h>

#include <errno.h>

#include "Wbhelper.h"

#include "wbreqhead.h"

#include "Wbresphead.h"

#include "wbresource.h"

/* HTTP request */

int WbService_Request(int wbcon)

{

    struct WBReqInfo wbreqinfo;

    int wbresource = 0;

  WbInitReqInfo(&wbreqinfo);

    /* Get HTTP request */

    if ( WbGet_Request(wbcon, &wbreqinfo) < 0 )

   return -1;

    /* Code to check existence of resource, permission

   to access, and update status code. */

    if ( wbreqinfo.status == 200 )

   if ( (wbresource = WbCheck_Resource(&wbreqinfo)) < 0 ) {

        if ( errno == EACCES )

       wbreqinfo.status = 401;

        else

       wbreqinfo.status = 404;

   }

    if ( wbreqinfo.type == FULL )

   WbOutput_HTTP_Headers(wbcon, &wbreqinfo);

   if ( wbreqinfo.status == 200 ) {

   if ( WbReturn_Resource (wbcon, wbresource, &wbreqinfo) )

        WbError_Quit("Something is wrong to return resource.");

    }

    else

   WbReturn_Error_Msg(wbcon, &wbreqinfo);

    if ( wbresource > 0 )

   if ( close(wbresource) < 0 )

        WbError_Quit("Error in closing the resource.");

    WbFreeReqInfo(&wbreqinfo);

    return 0;

}

wbreqhead.h

#ifndef WEBPG_REQHEAD_H

#define WEBPG_REQHEAD_H

/* User-defined data types */

enum WBReq_Method { GET, HEAD, UNSUPPORTED };

enum WBReq_Type   { SIMPLE, FULL };

struct WBReqInfo {

    enum WBReq_Method method;

    enum WBReq_Type   type;

    char           *referer;

    char           *useragent;

    char           *wbresource;

    int             status;

};

#define WBMAX_REQ_LINE         (1024)

/* Function prototypes */

int WBParse_HTTP_Header(char * Wbuffer, struct WBReqInfo * wbreqinfo);

int WbGet_Request      (int wbcon, struct WBReqInfo * wbreqinfo);

void WbInitReqInfo      (struct WBReqInfo * wbreqinfo);

void WbFreeReqInfo      (struct WBReqInfo * wbreqinfo);

#endif /* WEBPG_REQHEAD_H */

wbreqhead.c

#include <sys/time.h>

#include <stdlib.h>

#include <string.h>

#include <ctype.h>

#include "wbreqhead.h"

#include "wservreq.h"

#include "Wbhelper.h"

/* Code to parse the strings and update a request

    information */

int WBParse_HTTP_Header(char * Wbuffer, struct WBReqInfo * wbreqinfo) {

    static int Wbfirst_header = 1;

    char      *Wbtemp;

    char      *endptr;

    int        len;

    if ( Wbfirst_header == 1 )

   {

   if ( !strncmp(Wbuffer, "GET ", 4) ) {

        wbreqinfo->method = GET;

        Wbuffer += 4;

   }

   else if ( !strncmp(Wbuffer, "HEAD ", 5) ) {

        wbreqinfo->method = HEAD;

        Wbuffer += 5;

   }

   else {

        wbreqinfo->method = UNSUPPORTED;

        wbreqinfo->status = 501;

        return -1;

   }

   /* Skip to start */

   while ( *Wbuffer && isspace(*Wbuffer) )

        Wbuffer++;

   /* Calculate string length */

   endptr = strchr(Wbuffer, ' ');

   if ( endptr == NULL )

        len = strlen(Wbuffer);

   else

        len = endptr - Wbuffer;

   if ( len == 0 )

   {

        wbreqinfo->status = 400;

        return -1;

   }

   wbreqinfo->wbresource = calloc(len + 1, sizeof(char));

   strncpy(wbreqinfo->wbresource, Wbuffer, len);

   if ( strstr(Wbuffer, "HTTP/") )

        wbreqinfo->type = FULL;

   else

        wbreqinfo->type = SIMPLE;

   Wbfirst_header = 0;

   return 0;

    }

    endptr = strchr(Wbuffer, ':');

    if ( endptr == NULL ) {

   wbreqinfo->status = 400;

   return -1;

    }

    Wbtemp = calloc( (endptr - Wbuffer) + 1, sizeof(char) );

    strncpy(Wbtemp, Wbuffer, (endptr - Wbuffer));

    WStrUpper(Wbtemp);

    Wbuffer = endptr + 1;

    while ( *Wbuffer && isspace(*Wbuffer) )

   ++Wbuffer;

    if ( *Wbuffer == '' )

        return 0;

    if ( !strcmp(Wbtemp, "USER-AGENT") ) {

        wbreqinfo->useragent = malloc( strlen(Wbuffer) + 1 );

        strcpy(wbreqinfo->useragent, Wbuffer);

    }

    else if ( !strcmp(Wbtemp, "REFERER") ) {

        wbreqinfo->referer = malloc( strlen(Wbuffer) + 1 );

        strcpy(wbreqinfo->referer, Wbuffer);

    }

    free(Wbtemp);

    return 0;

}

int WbGet_Request(int wbcon, struct WBReqInfo * wbreqinfo) {

    char   Wbuffer[WBMAX_REQ_LINE] = {0};

    int    Wbrval;

    fd_set Wbfds;

    struct timeval Wbtv;

    /* Timeout to 5 seconds */

    Wbtv.Wbtv_sec = 5;

    Wbtv.Wbtv_usec = 0;

    do

   {

   FD_ZERO(&Wbfds);

   FD_SET (wbcon, &Wbfds);

   Wbrval = select(wbcon + 1, &Wbfds, NULL, NULL, &Wbtv);

   if ( Wbrval < 0 ) {

        WbError_Quit("Error calling select() in get_request()");

   }

   else if ( Wbrval == 0 ) {

        return -1;

   }

   else {

        WReadline(wbcon, Wbuffer, WBMAX_REQ_LINE - 1);

        WTrim(Wbuffer);

        if ( Wbuffer[0] == '' )

       break;

        if ( WBParse_HTTP_Header(Wbuffer, wbreqinfo) )

       break;

   }

    } while ( wbreqinfo->type != SIMPLE );

    return 0;

}

/* Request information structure initialization */

void WbInitReqInfo(struct WBReqInfo * wbreqinfo) {

    wbreqinfo->useragent = NULL;

    wbreqinfo->referer   = NULL;

    wbreqinfo->wbresource = NULL;

    wbreqinfo->method    = UNSUPPORTED;

    wbreqinfo->status    = 200;       

}

/* Free the allocated memory */

void WbFreeReqInfo(struct WBReqInfo * wbreqinfo)

{

    if ( wbreqinfo->useragent )

   free(wbreqinfo->useragent);

    if ( wbreqinfo->referer )

   free(wbreqinfo->referer);

    if ( wbreqinfo->wbresource )

   free(wbreqinfo->wbresource);

}

Wbresphead.h

#ifndef WbPG_RESPHEAD_H

#define WbPG_RESPHEAD_H

#include "wbreqhead.h"

/* Function prototypes */

int WbOutput_HTTP_Headers(int wbcon, struct WBReqInfo * wbreqinfo);

#endif /* WbPG_RESPHEAD_H */

Wbresphead.c

#include <unistd.h>

#include <stdio.h>

#include "Wbresphead.h"

#include "Wbhelper.h"

/* Output of HTTP response headers */

int WbOutput_HTTP_Headers(int wbcon, struct WBReqInfo * wbreqinfo) {

    char Wbuffer[100];

    sprintf(Wbuffer, "HTTP/1.0 %d OK ", wbreqinfo->status);

    WWriteline(wbcon, Wbuffer, strlen(Wbuffer));

    WWriteline(wbcon, "Server: PGWebServ v0.1 ", 24);

    WWriteline(wbcon, "Content-Type: text/html ", 25);

    WWriteline(wbcon, " ", 2);

    return 0;

}

wbresource.h

#ifndef WbPG_RESOURCE_H

#define WbPG_RESOURCE_H

#include "wbreqhead.h"

/* Function prototypes */

int WbReturn_Resource (int wbcon, int wbresource, struct WBReqInfo * wbreqinfo);

int WbCheck_Resource (struct WBReqInfo * wbreqinfo);

int WbReturn_Error_Msg(int wbcon, struct WBReqInfo * wbreqinfo);

#endif /* WbPG_RESOURCE_H */

wbresource.c

#include <unistd.h>

#include <fcntl.h>

#include <string.h>

#include <stdio.h>

#include "wbresource.h"

#include "wbreqhead.h"

#include "Wbhelper.h"

// server root

static char server_root[1000] = "/home/httpd/html";

/* Return a resource */

int WbReturn_Resource (int wbcon, int wbresource, struct WBReqInfo * wbreqinfo)

{

    char Wc;

    int Wi;

    while ( (Wi = read(wbresource, &Wc, 1)) ) {

   if ( Wi < 0 )

        WbError_Quit("Error reading from file.");

   if ( write(wbcon, &Wc, 1) < 1 )

        WbError_Quit("Error sending file.");

    }

    return 0;

}

/* Method to open a resource and determines the cause of failure    */

int WbCheck_Resource(struct WBReqInfo * wbreqinfo)

{

    WCleanURL(wbreqinfo->wbresource);

    strcat(server_root, wbreqinfo->wbresource);

    return open(server_root, O_RDONLY);

}

/* Method to return an error message */

int WbReturn_Error_Msg(int wbcon, struct WBReqInfo * wbreqinfo)

{

    char Wbuffer[100];

    sprintf(Wbuffer, "<HTML> <HEAD> <TITLE>Server Error %d</TITLE> "

                "</HEAD> ", wbreqinfo->status);

    WWriteline(wbcon, Wbuffer, strlen(Wbuffer));

    sprintf(Wbuffer, "<BODY> <H1>Server Error %d</H1> ", wbreqinfo->status);

    WWriteline(wbcon, Wbuffer, strlen(Wbuffer));

    sprintf(Wbuffer, "<P>The request could not be completed.</P> "

                "</BODY> </HTML> ");

    WWriteline(wbcon, Wbuffer, strlen(Wbuffer));

    return 0;

}

Wbhelper.h

#ifndef WBPG_HELPER_H

#define WBPG_HELPER_H

#include <unistd.h>

/* Function prototypes */

void    WbError_Quit(char const * msg);

int     WTrim      (char * Wbuffer);

int     WStrUpper (char * Wbuffer);

void    WCleanURL (char * Wbuffer);

ssize_t WReadline (int Wsockd, void *Wvptr, size_t maxlen);

ssize_t WWriteline (int Wsockd, const void *Wvptr, size_t n);

#define LISTENQ          (1024)

#endif /* WBPG_HELPER_H */

Wbhelper.c

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <ctype.h>

#include "Wbhelper.h"

/* Method to print an error message and quit */

void WbError_Quit(char const * msg)

{

    fprintf(stderr, "WEBSERV: %s ", msg);

    exit(EXIT_FAILURE);

}

/* Method to read a line from a socket */

ssize_t WReadline(int Wsockd, void *Wvptr, size_t maxlen)

{

    ssize_t n, wrc;

    char    Wc, *Wbuffer;

    Wbuffer = Wvptr;

    for ( n = 1; n < maxlen; n++ ) {

   if ( (wrc = read(Wsockd, &Wc, 1)) == 1 ) {

        *Wbuffer++ = Wc;

        if ( Wc == ' ' )

       break;

   }

   else if ( wrc == 0 ) {

        if ( n == 1 )

       return 0;

        else

       break;

   }

   else {

        if ( errno == EINTR )

       continue;

        WbError_Quit("Error in WReadline()");

   }

    }

    *Wbuffer = 0;

    return n;

}

/* Method to write a line to a socket */

ssize_t WWriteline(int Wsockd, const void *Wvptr, size_t n) {

    size_t      wnleft;

    ssize_t     wnwritten;

    const char *Wbuffer;

    Wbuffer = Wvptr;

    wnleft = n;

    while ( wnleft > 0 ) {

   if ( (wnwritten = write(Wsockd, Wbuffer, wnleft)) <= 0 ) {

        if ( errno == EINTR )

       wnwritten = 0;

        else

       WbError_Quit("Error in WWriteline()");

   }

   wnleft -= wnwritten;

   Wbuffer += wnwritten;

    }

    return n;

}

/* Method to remove white space in a string */

int WTrim(char * Wbuffer)

{

    int n = strlen(Wbuffer) - 1;

    while ( !isalnum(Wbuffer[n]) && n >= 0 )

   Wbuffer[n--] = '';

    return 0;

}

/* Method to convert a string into uppercase */

int WStrUpper(char * Wbuffer)

{

    while ( *Wbuffer )

   {

   *Wbuffer = toupper(*Wbuffer);

   ++Wbuffer;

    }

    return 0;

}

/* Method to cleans url encoded string */

void WCleanURL(char * Wbuffer) {

   char asciinum[3] = {0};

    int Wi = 0, Wc;

    while ( Wbuffer[Wi] ) {

   if ( Wbuffer[Wi] == '+' )

        Wbuffer[Wi] = ' ';

   else if ( Wbuffer[Wi] == '%' ) {

        asciinum[0] = Wbuffer[Wi+1];

        asciinum[1] = Wbuffer[Wi+2];

        Wbuffer[Wi] = strtol(asciinum, NULL, 16);

        Wc = Wi+1;

        do {

       Wbuffer[Wc] = Wbuffer[Wc+2];

        } while ( Wbuffer[2+(Wc++)] );

   }

   ++Wi;

    }

}