/** * server.c, copyright 2001 Steve Gribble * * The server is a single-threaded program. First, it opens * up a "listening socket" so that clients can connect to * it. Then, it enters a tight loop; in each iteration, it * accepts a new connection from the client, reads a request, * computes for a while, sends a response, then closes the * connection. */ #include #include #include #include #include #include #include #include "SocketLibrary/socklib.h" #include "common.h" #include "threadpool.h" //#define DEBUG const int TRIGGER_COUNT = 50; /* number of dispatches between time checks */ extern int errno; int setup_listen(char *socketNumber); char *read_request(int fd); char *process_request(char *request, int *response_length, int *num_loops); void send_response(int fd, char *response, int response_length); void run_server(int socket, int pool_size, int num_loops, int checks); void process_connection(void * sockp); struct proc_conn_args { int socket; /* socket to talk on */ int num_loops; /* number of loop iterations (simulated work) to do */ }; /** * This program should be invoked as * "./server ", * * where * poolsize is number of threads in threadpool * num_loops is the number of loops in the 'process_request' fn * checks is the number of time checks (datapoints) before * shutting down. If 0 or omitted, run indefinitely. * * For example, "./server 4342 10 50 10000" */ int main(int argc, char **argv) { int socket_listen; int num_loops; /* Number of loop iterations in 'process_request' */ int pool_size; /* Number of threads in pool */ int checks = 0; /* Number of datapoints to take before exiting */ if (argc != 4 && argc != 5) { fprintf(stderr, "(SERVER): Invoke as './server socknum poolsize num_loops [checks]'\n"); fprintf(stderr, "(SERVER): for example, './server 4444 8 1000 100'\n"); exit(-1); } /* * Get args 'pool_size', 'num_loops', and 'checks'; and sanity-check them */ if ((pool_size = atoi(argv[2])) < 1 ) { fprintf(stderr, "(SERVER): pool size must be > 1\n"); exit(-1); } if ((num_loops = atoi(argv[3])) < 1) { fprintf(stderr, "(SERVER): number of loops must be > 1\n"); exit(-1); } if (4 == argc) checks = 0; else /* argc is 5 */ if ((checks = atoi(argv[4])) < 0) { fprintf(stderr, "(SERVER): checks must be >= 0\n"); exit(-1); } /* * Set up the 'listening socket'. This establishes a network * IP_address:port_number that other programs can connect with. */ socket_listen = setup_listen(argv[1]); #ifdef DEBUG fprintf(stderr, "(SERVER) Listening on socket %d\n", socket_listen); fflush(stderr); #endif run_server(socket_listen, pool_size, num_loops, checks); close(socket_listen); return(0); } /** * This function runs the server, with configuration given by: * socket : socket to listen on * pool_size: number of threads in the pool * num_loops: number of loop iterations in 'process_connection' * checks : number of datapoints (time checks) to take */ void run_server(int socket, int pool_size, int num_loops, int checks) { int socket_talk; /* socket we reply on */ threadpool pool; /* pool of 'pool_size' threads */ int dispatch_count; /* # of dispatches since last sample */ struct timeval then, now, diff; /* benchmarking timestamps */ int do_forever; /* flag: loop forever? */ struct proc_conn_args pcargs; /* holds args for 'process_connection' */ int count = checks; /* Set up threadpool */ if ( NULL == (pool = create_threadpool(pool_size))) { fprintf(stderr, "(SERVER): Unable to create threadpool.\n"); exit(-1); } /* Set up do_forever */ /* if checks == 0, do_forever = 1 (true); otherwise, the number of */ /* times through the loop to follow is set by checks. */ do_forever = (checks==0); /* MAIN LOOP: do this 'checks' times, or forever if checks == 0 * 1) Wait on the socket for a new connection to arrive. * 2) Get a socket for replying. * 3) Dispatch a thread to process request and make reply. */ gettimeofday(&then, NULL); /* init throughput vars */ dispatch_count = TRIGGER_COUNT; while(do_forever || (checks > 0)) { socket_talk = saccept(socket); if (socket_talk < 0) { fprintf(stderr, "(SERVER) Connection failed because of "); perror(""); exit(1); } /* Set up arg struct for 'process connection' */ pcargs.socket = socket_talk; pcargs.num_loops = num_loops; //fprintf(stderr,"Dispatching\n"); dispatch(pool, process_connection, &pcargs); //fprintf(stderr,"Dispatch ends\n"); //if ((count % 10) == 0) //{ //fprintf(stderr,"Count remaining = %d\n",count); //} if (count == 100 ) gettimeofday(&then,NULL); if (count == 50 ) { double seconds; //elapsed time in seconds between then and now gettimeofday(&now,NULL); timersub(&now, &then, &diff); seconds = diff.tv_sec + diff.tv_usec * 0.000001; fprintf(stdout, "Loops: %6d Threads: %2d Dispatches/sec: %10.3f\n", num_loops, pool_size, 50/seconds); } /* reset dispatch count, update checks left*/ dispatch_count = TRIGGER_COUNT; --checks; --count; } } /** * This function implements the action of a thread when processing * a new connection. Its type must be 'dispatch_fn', defined as * void (*dispatch_fn)(void *). The argument is a socket identifier, * that is, an int. * See comments for main() for explanation of steps. */ void process_connection(void * args) { char *request = NULL; char *response = NULL; struct proc_conn_args *myargs = (struct proc_conn_args *)args; int socket_talk = myargs->socket; int num_loops = myargs->num_loops; #ifdef DEBUG fprintf(stderr, "(SERVER) Processing request on socket %d\n", socket_talk); #endif request = read_request(socket_talk); if (request != NULL) { int response_length; response = process_request(request, &response_length, &num_loops); if (response != NULL) { send_response(socket_talk, response, response_length); } } close(socket_talk); /* clean up allocated memory, if any */ if (request != NULL) free(request); if (response != NULL) free(response); } /** * This function accepts a string of the form "5654", and opens up * a listening socket on the port associated with that string. In * case of error, this function simply bonks out. */ int setup_listen(char *socketNumber) { int socket_listen; if ((socket_listen = slisten(socketNumber)) < 0) { perror("(SERVER): slisten"); exit(1); } return socket_listen; } /** * This function reads a request off of the given socket. * This function is thread-safe. */ char *read_request(int fd) { char *request = (char *) malloc(REQUEST_SIZE*sizeof(char)); int ret; if (request == NULL) { fprintf(stderr, "(SERVER): out of memory!\n"); exit(-1); } ret = correct_read(fd, request, REQUEST_SIZE); if (ret != REQUEST_SIZE) { free(request); request = NULL; } return request; } /** * This function crunches on a request, returning a response. * This is where all of the hard work happens. * This function is thread-safe. */ char *process_request(char *request, int *response_length, int *num_loops) { char *response = (char *) malloc(RESPONSE_SIZE*sizeof(char)); int loops = *num_loops; int i,j; if (response == NULL){ fprintf(stderr, "(SERVER): out of memory!\n"); exit(-1); } // just do some mindless character munging here for (i=0; i