Insight into something.
//////////////////////////////////////////////////////////////////////////////// /** * @file guthrn.c * @brief A file storage server implemented in C. * * The server will be multithreaded using the pthreads library. In particular, * clients connect via sockets. The server must hand off the incoming * connections to dedicated threads. You can either create threads on-demand or * manage a pool of available threads. * * @title guthrn * @author Nicholas Guthrie * @email guthrn@rpi.edu * @web http//nickguthrie.com * @created April 22, 2013 * * Compile with: gcc -g -o file-server file-server.c -lpthread */ //////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------ :Libraries: // _ _ _ _ // | | (_) |__ _ _ __ _ _ _(_)___ ___ // | |__| | '_ \ '_/ _` | '_| / -_|_-< // |____|_|_.__/_| \__,_|_| |_\___/__/ // // ----------------------------------------------------------------------------- #include <stdio.h> // for printf() #include <stdlib.h> // for exit() #include <string.h> // for strcpy(),strerror() and strlen() #include <fcntl.h> // for file i/o constants #include <sys/stat.h> // for file i/o constants #include <errno.h> // FOR BSD UNIX/LINUX #include <sys/types.h> // #include <netinet/in.h> // #include <sys/socket.h> // for socket system calls #include <arpa/inet.h> // for socket system calls (bind) #include <sched.h> #include <pthread.h> // P-thread implementation #include <signal.h> // for signal #include <semaphore.h> // for p-thread semaphores //--------------------------------------------------------------------- :Global: // ___ _ _ _ // / __| |___| |__ __ _| | // | (_ | / _ \ '_ \/ _` | | // \___|_\___/_.__/\__,_|_| // // ----------------------------------------------------------------------------- #define BUF_SIZE 1024 // buffer size in bytes #define PEND_CONNECTIONS 100 // pending connections to hold #define TRUE 1 #define FALSE 0 #define NTHREADS 5 /* Number of child threads */ #define NUM_LOOPS 10 /* Number of local loops */ #define SCHED_INTVL 5 /* thread scheduling interval */ #define HIGHPRIORITY 10 #define D_STORAGE ".storage" // global variables ------------------------------------------------------------ sem_t thread_sem[NTHREADS]; int next_thread; int can_run; int i_stopped[NTHREADS]; unsigned int client_s; // Client socket descriptor //----- HTTP response messages ------------------------------------------------- #define YESFILE "FILE EXISTS" #define NOFILE "NO SUCH FILE" #define ACK "ACK" #define ERROR "ERROR" //------------------------------------------------------------------ :Functions: // ___ _ _ // | __| _ _ _ __| |_(_)___ _ _ ___ // | _| || | ' \/ _| _| / _ \ ' \(_-< // |_| \_,_|_||_\__|\__|_\___/_||_/__/ // // // ----------------------------------------------------------------------------- void print_id() { fprintf(stdout, "\n[thread %d] ", getpid()); } //////////////////////////////////////////////////////////////////////////////// /** * @brief Child thread implementation * @param[in] arg * @return void* */ //////////////////////////////////////////////////////////////////////////////// void *my_thread(void * arg) { unsigned int myClient_s; //copy socket // other local variables --------------------------------------------------- char in_buf[BUF_SIZE]; // input buffer for GET resquest char out_buf[BUF_SIZE]; // output buffer for HTML response char fname[100]; // file name char command[10]; // command given by user unsigned int fh; // file handle (file descriptor) unsigned int buf_len; // buffer length for file reads unsigned int retcode; // return code struct stat st; // stores file information myClient_s = *(unsigned int *)arg; // copy the socket // receive the command int pos = 0; while(1) { retcode = recv(client_s, in_buf, 1, 0); if (retcode < 0) { printf("recv error detected ...\n"); return; } if(in_buf[0] == ' ') break; command[pos++] = in_buf[0]; } command[pos] = '\0'; // receive the file name pos = 0; while(1) { retcode = recv(client_s, in_buf, 1, 0); if (retcode < 0) { printf("recv error detected ...\n"); return; } if(in_buf[0] == '\n' || in_buf[0] == ' ') break; fname[pos++] = in_buf[0]; } fname[pos] = '\0'; print_id(); fprintf(stdout, "Rcvd: %s %s", command, fname); fflush(stdout); char cor_fname[100] = D_STORAGE; strcat(cor_fname, "/"); strcat(cor_fname, fname); strcpy(fname, cor_fname); ////////////////////////////////////////////////////////////////////// // ADD <filename> <bytes>\n<file-contents> // - add <filename> to the storage server // - IF the file already exists, return "FILE EXISTS" error // - return "ACK" if successful, "ERROR" if not ////////////////////////////////////////////////////////////////////// if(strcmp(command, "ADD") == 0) { // File Exists if( access( fname, F_OK ) != -1 ) { // Send Message Back print_id(); fprintf(stdout, "Sent: %s", YESFILE); fflush(stdout); strcpy(out_buf, YESFILE); write(client_s, out_buf, strlen(out_buf) ); } // File doesn't exist else { // Read the File Size char sf_size[50]; pos = 0; while(1) { retcode = recv(client_s, in_buf, 1, 0); if ( retcode < 0 ) { puts("recv error detected ...\n"); return; } if(in_buf[0] == '\n') break; sf_size[pos++] = in_buf[0]; } sf_size[pos] = '\0'; int file_size = (int) atoi(sf_size); fprintf(stdout, " %d", file_size); fflush(stdout); // Write File ------------------------------------------------------ FILE *wfile; wfile = fopen(fname,"wb+"); // Recieve File Data and Write on Server int BUFFSIZ = 100; int remain_data = file_size; int len; char * buffer = (char*)malloc(BUFFSIZ * sizeof(char)); while (((len = recv(client_s, buffer, BUFSIZ, 0)) > 0) && (remain_data > 0)) { fwrite(buffer, sizeof(char), len, wfile); remain_data -= len; //fprintf(stdout, "Receive %d bytes and we hope :- %d bytes\n", len, remain_data); } fclose(wfile); //free(buffer); print_id(); fprintf(stdout, "Transferred file (%d bytes)", file_size); fflush(stdout); // Send Success Message to Client print_id(); fprintf(stdout, "Sent: %s %d", ACK, file_size); fflush(stdout); strcpy(out_buf, ACK); write(client_s, out_buf, strlen(out_buf) ); } } ////////////////////////////////////////////////////////////////////// // UPDATE <filename> <bytes>\n<file-contents> // - update existing file <filename> on the storage server // - the file's contents will be overwritten // - IF the file does not already exist, return "NO SUCH FILE" error // - return "ACK" if successful, "ERROR" if not ////////////////////////////////////////////////////////////////////// else if(strcmp(command, "UPDATE") == 0) { // File Exists if( access( fname, F_OK ) != -1 ) { // Read the File Size char sf_size[50]; pos = 0; while(1) { retcode = recv(client_s, in_buf, 1, 0); if ( retcode < 0 ) { puts("recv error detected ...\n"); return; } if(in_buf[0] == '\n') break; sf_size[pos++] = in_buf[0]; } sf_size[pos] = '\0'; int file_size = (int) atoi(sf_size); fprintf(stdout, " %d", file_size); // Write File ------------------------------------------------------ FILE *wfile; wfile = fopen(fname,"wb"); // Recieve File Data and Write on Server int BUFFSIZ = 100; int remain_data = file_size; int len; char * buffer = (char*)malloc(BUFFSIZ * sizeof(char)); while (((len = recv(client_s, buffer, BUFSIZ, 0)) > 0) && (remain_data > 0)) { fwrite(buffer, sizeof(char), len, wfile); remain_data -= len; } print_id(); fprintf(stdout, "Transferred file (%d bytes)", file_size); fflush(stdout); // Send Message Back print_id(); fprintf(stdout, "Sent: %s", ACK); fflush(stdout); strcpy(out_buf, ACK); write(client_s, out_buf, strlen(out_buf) ); } // File doesn't exist else { print_id(); fprintf(stdout, "Sent: %s", NOFILE); fflush(stdout); strcpy(out_buf, NOFILE); write(client_s, out_buf, strlen(out_buf) ); } } ////////////////////////////////////////////////////////////////////// // READ <filename>\n // - server returns the length (in bytes) and content of <filename> // - note that this does NOT remove the file on the server // - IF the file does not exist, return "NO SUCH FILE" error // - RETURN "ACK" if successful, "ERROR" if not // - IF "ACK" is sent, follow it with file length and file as follows: // ACK <bytes>\n<file-contents> ////////////////////////////////////////////////////////////////////// else if(strcmp(command, "READ") == 0) { // File Exists if( access( fname, F_OK ) != -1 ) { FILE *xfile; //File to Read from Server char buf[150]; //Temporary Storage Buffer xfile = fopen(fname, "rb"); if (xfile == NULL) { perror ("The following error occurred"); fprintf( stdout, "Value of errno: %d\n", errno ); } // Get File Size stat(fname, &st); int file_size = st.st_size; //Size of the file as sent char str[100]; //File Size in a String sprintf(str, "%d", file_size); //convert int to string // Add to buf strcat(buf, "ACK "); strcat(buf, str); strcat(buf, "\n"); // Read the File Into An Array char *bdata = (char*)malloc(strlen(buf) * sizeof(char) + file_size * sizeof(char)); char *tdata = (char*)malloc(file_size * sizeof(char)); int pos = 0; int c; while ( (c = fgetc(xfile)) != EOF ) { tdata[pos++] = (char) c; } strcat(bdata, buf); //copy command and size strcat(bdata, tdata); //copy file contents // Write to Server write( client_s, bdata, strlen( bdata ) ); print_id(); fprintf(stdout, "Transferred file (%d bytes)", file_size); fflush(stdout); // Close Data fclose(xfile); free(bdata); free(tdata); } else { print_id(); fprintf(stdout, "Sent: %s", ERROR); fflush(stdout); strcpy(out_buf, ERROR); write(client_s, out_buf, strlen(out_buf) ); return; } } //////////////////// // Exiting //////////////////// close(client_s); // close the client connection pthread_exit(NULL); } //////////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- :Main: // __ __ _ // | \/ |__ _(_)_ _ // | |\/| / _` | | ' \ // |_| |_\__,_|_|_||_| // // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// int main ( int argc, char* argv[] ) { // Variables --------------------------------------------------------------- unsigned int server_s; // server socket descriptor struct sockaddr_in server_addr; // server internet address struct sockaddr_in client_addr; // client internet address struct in_addr client_ip_addr; // client IP address int addr_len; // internet address length unsigned int ids; // holds thread arguments pthread_attr_t attr; // pthread attributes pthread_t threads; // thread ID (for os) int port_num; // Create Storage Directory struct stat info; if(lstat(D_STORAGE,&info) != 0) { // doesn't exist if(errno == ENOENT) { char new_dir[]= D_STORAGE; if (mkdir(new_dir, S_IRWXU|S_IRGRP|S_IXGRP) != 0) perror("mkdir() error"); } } // Start Server printf("Started file-server\n"); if (argc == 2) { sscanf(argv[1], "%d", &port_num); printf("Listening on port %d", port_num); if(port_num < 8000 || port_num > 9000) { perror("ERROR: Port number must from 8000-9000.\n"); return -1; } } else { perror("ERROR: Port number must from 8000-9000.\n"); return -1; } // create a new socket server_s = socket(AF_INET, SOCK_STREAM, 0); // fill-in address information, and then bind it server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port_num); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(server_s, (struct sockaddr *)&server_addr, sizeof(server_addr)); // listen for connections and then accept listen( server_s, PEND_CONNECTIONS ); //////////////////////////////////////// // Main Server Loop //////////////////////////////////////// pthread_attr_init(&attr); while(TRUE) { // wait for the next client to arrive addr_len = sizeof( client_addr ); client_s = accept(server_s, (struct sockaddr *)&client_addr, &addr_len); // error handling when creating socket if (client_s == FALSE) { printf("ERROR - Unable to create socket \n"); exit(FALSE); } // create child thread else { printf("\nRecieved incoming connecint from <client-hostname>"); ids = client_s; pthread_create (&threads, &attr,my_thread,&ids); } } //////////////////// // Exiting //////////////////// close (server_s); // close the primary socket return (TRUE); }