Question
/*-------------------------------------------------------------------------* *--- ---* *--- mathClientServer.h ---* *--- ---* *--- This file declares C functions and constants common to both ---* *--- mathClient.c and mathServer.c. ---*
/*-------------------------------------------------------------------------* *--- ---* *--- mathClientServer.h ---* *--- ---* *--- This file declares C functions and constants common to both ---* *--- mathClient.c and mathServer.c. ---* *--- ---* *--- ---- ---- ---- ---- ---- ---- ---- ---- ---* *--- ---* *--- Version 1.0 2017 February 20 Joseph Phillips ---* *--- ---* *-------------------------------------------------------------------------*/ //--- Header file inclusion ---// #include#include #include #include // For unlink() #include // For waitpid(), opendir() #include // For waitpid() #include // For opendir(), readdir(), closedir() #include // For socket() #include // For sockaddr_in and htons() #include // For getaddrinfo() #include // For errno var #include // For open(), read(),write(), stat() #include // and close() //--- Definition of constants: ---// #define BUFFER_LEN 256 #define DIR_CMD_CHAR 'l' #define READ_CMD_CHAR 'r' #define WRITE_CMD_CHAR 'w' #define DELETE_CMD_CHAR 'd' #define CALC_CMD_CHAR 'c' #define QUIT_CMD_CHAR 'q' const int MIN_FILE_NUM = 0; const int MAX_FILE_NUM = 63;
/*-------------------------------------------------------------------------* *--- ---* *--- mathClient.c ---* *--- ---* *--- This file defines a C program that gets file-sys commands ---* *--- from the user, and sends them to a server via a socket, waits ---* *--- for a reply, and outputs the response to the user. ---* *--- ---* *--- ---- ---- ---- ---- ---- ---- ---- ---- ---* *--- ---* *--- Version 1.0 2017 February 20 Joseph Phillips ---* *--- ---* *-------------------------------------------------------------------------*/ // Compile with: // $ gcc mathServer.c -o mathServer -lpthread //--- Header file inclusion ---// #include "mathClientServer.h" //--- Definition of constants: ---// #define DEFAULT_HOSTNAME "localhost.localdomain" //--- Definition of functions: ---// // PURPOSE: To ask the user for the name and the port of the server. The // server name is returned in 'url' up to length 'urlLen'. The port // number is returned in '*portPtr'. No return value. void obtainUrlAndPort (int urlLen, char* url, int* portPtr ) { // I. Application validity check: if ( (url == NULL) || (portPtr == NULL) ) { fprintf(stderr,"BUG: NULL ptr to obtainUrlAndPort() "); exit(EXIT_FAILURE); } if (urlLen <= 1) { fprintf(stderr,"BUG: Bad string length to obtainUrlAndPort() "); exit(EXIT_FAILURE); } // II. Get server name and port number: // II.A. Get server name: printf("Machine name [%s]? ",DEFAULT_HOSTNAME); fgets(url,urlLen,stdin); char* cPtr = strchr(url,' '); if (cPtr != NULL) *cPtr = '\0'; if (url[0] == '\0') strncpy(url,DEFAULT_HOSTNAME,urlLen); // II.B. Get port numbe: char buffer[BUFFER_LEN]; printf("Port number? "); fgets(buffer,BUFFER_LEN,stdin); *portPtr = strtol(buffer,NULL,10); // III. Finished: } // PURPOSE: To attempt to connect to the server named 'url' at port 'port'. // Returns file-descriptor of socket if successful, or '-1' otherwise. int attemptToConnectToServer (const char* url, int port ) { // I. Application validity check: if (url == NULL) { fprintf(stderr,"BUG: NULL ptr to attemptToConnectToServer() "); exit(EXIT_FAILURE); } // II. Attempt to connect to server: // II.A. Create a socket: int socketDescriptor = socket(AF_INET, // AF_INET domain SOCK_STREAM, // Reliable TCP 0); // II.B. Ask DNS about 'url': struct addrinfo* hostPtr; int status = getaddrinfo(url,NULL,NULL,&hostPtr); if (status != 0) { fprintf(stderr,gai_strerror(status)); return(-1); } // II.C. Attempt to connect to server: struct sockaddr_in server; // Clear server datastruct memset(&server, 0, sizeof(server)); // Use TCP/IP server.sin_family = AF_INET; // Tell port # in proper network byte order server.sin_port = htons(port); // Copy connectivity info from info on server ("hostPtr") server.sin_addr.s_addr = ((struct sockaddr_in*)hostPtr->ai_addr)->sin_addr.s_addr; status = connect(socketDescriptor,(struct sockaddr*)&server,sizeof(server)); if (status < 0) { fprintf(stderr,"Could not connect %s:%d ",url,port); return(-1); } // III. Finished: return(socketDescriptor); } // PURPOSE: To ask the user for a file number, and to return that number. // No parameters. int getFileNumber () { // I. Application validity check: // II. Get number: char buffer[BUFFER_LEN]; int choice; do { printf(" File number (%d-%d)? ",MIN_FILE_NUM,MAX_FILE_NUM); fgets(buffer,BUFFER_LEN,stdin); choice = strtol(buffer,NULL,10); } while ( (choice < MIN_FILE_NUM) || (choice > MAX_FILE_NUM) ); // III. Finished: return(choice); } // PURPOSE: To const char* getText () { // I. Application validity check: // II. : static char buffer[BUFFER_LEN-2*sizeof(int)]; printf(" Expression? "); fgets(buffer,BUFFER_LEN-2*sizeof(int),stdin); // III. Finished: return(buffer); } // PURPOSE: To do the work of the application. Gets letter from user, sends // it to server over file-descriptor 'fd', and prints returned text. // No return value. void communicateWithServer (int fd ) { // I. Application validity check: // II. Do work of application: // II.A. Get letter from user: char buffer[BUFFER_LEN+1]; int shouldContinue = 1; while (shouldContinue) { int choice; do { printf ("What would you like to do: " "(1) List files " "(2) Read a math file " "(3) Write a math file " "(4) Calculate a math file " "(5) Delete a math file " "(0) Quit " "Your choice? " ); fgets(buffer,BUFFER_LEN,stdin); choice = strtol(buffer,NULL,10); } while ( (choice < 0) || (choice > 5) ); switch (choice) { case 0 : shouldContinue = 0; snprintf(buffer,BUFFER_LEN,"%c",QUIT_CMD_CHAR); break; case 1 : snprintf(buffer,BUFFER_LEN,"%c",DIR_CMD_CHAR); break; case 2 : snprintf(buffer,BUFFER_LEN,"%c %d",READ_CMD_CHAR,getFileNumber()); break; case 3 : snprintf(buffer,BUFFER_LEN,"%c %d \"%s\"", WRITE_CMD_CHAR,getFileNumber(),getText() ); break; case 4 : snprintf(buffer,BUFFER_LEN,"%c %d",CALC_CMD_CHAR,getFileNumber()); break; case 5 : snprintf(buffer,BUFFER_LEN,"%c %d",DELETE_CMD_CHAR,getFileNumber()); break; } printf("Sending \"%s\" ",buffer); write(fd,buffer,strlen(buffer)+1); read (fd,buffer,BUFFER_LEN); printf("%s ",buffer); } // III. Finished: } // PURPOSE: To do the work of the client. Ignores command line parameters. // Returns 'EXIT_SUCCESS' to OS on success or 'EXIT_FAILURE' otherwise. int main () { char url[BUFFER_LEN]; int port; int fd; obtainUrlAndPort(BUFFER_LEN,url,&port); fd = attemptToConnectToServer(url,port); if (fd < 0) exit(EXIT_FAILURE); communicateWithServer(fd); close(fd); return(EXIT_SUCCESS); }
/*-------------------------------------------------------------------------* *--- ---* *--- mathServer.c ---* *--- ---* *--- This file defines a C program that gets file-sys commands ---* *--- from client via a socket, executes those commands in their own ---* *--- threads, and returns the corresponding output back to the ---* *--- client. ---* *--- ---* *--- ---- ---- ---- ---- ---- ---- ---- ---- ---* *--- ---* *--- Version 1.0 2017 February 20 Joseph Phillips ---* *--- ---* *-------------------------------------------------------------------------*/ // Compile with: // $ gcc mathServer.c -o mathServer -lpthread //--- Header file inclusion ---// #include "mathClientServer.h" #include// For perror() #include // For pthread_create() //--- Definition of constants: ---// #define STD_OKAY_MSG "Okay" #define STD_ERROR_MSG "Error doing operation" #define STD_BYE_MSG "Good bye!" #define THIS_PROGRAM_NAME "mathServer" #define FILENAME_EXTENSION ".bc" #define OUTPUT_FILENAME "out.txt" #define ERROR_FILENAME "err.txt" #define CALC_PROGNAME "/usr/bin/bc" const int ERROR_FD = -1; //--- Definition of functions: ---// // PURPOSE: To run the server by 'accept()'-ing client requests from // 'listenFd' and doing them. void doServer (int listenFd ) { // I. Application validity check: // II. Server clients: pthread_t threadId; pthread_attr_t threadAttr; int threadCount = 0; // YOUR CODE HERE } // PURPOSE: To decide a port number, either from the command line arguments // 'argc' and 'argv[]', or by asking the user. Returns port number. int getPortNum (int argc, char* argv[] ) { // I. Application validity check: // II. Get listening socket: int portNum; if (argc >= 2) portNum = strtol(argv[1],NULL,0); else { char buffer[BUFFER_LEN]; printf("Port number to monopolize? "); fgets(buffer,BUFFER_LEN,stdin); portNum = strtol(buffer,NULL,0); } // III. Finished: return(portNum); } // PURPOSE: To attempt to create and return a file-descriptor for listening // to the OS telling this server when a client process has connect()-ed // to 'port'. Returns that file-descriptor, or 'ERROR_FD' on failure. int getServerFileDescriptor (int port ) { // I. Application validity check: // II. Attempt to get socket file descriptor and bind it to 'port': // II.A. Create a socket int socketDescriptor = socket(AF_INET, // AF_INET domain SOCK_STREAM, // Reliable TCP 0); if (socketDescriptor < 0) { perror(THIS_PROGRAM_NAME); return(ERROR_FD); } // II.B. Attempt to bind 'socketDescriptor' to 'port': // II.B.1. We'll fill in this datastruct struct sockaddr_in socketInfo; // II.B.2. Fill socketInfo with 0's memset(&socketInfo,'\0',sizeof(socketInfo)); // II.B.3. Use TCP/IP: socketInfo.sin_family = AF_INET; // II.B.4. Tell port in network endian with htons() socketInfo.sin_port = htons(port); // II.B.5. Allow machine to connect to this service socketInfo.sin_addr.s_addr = INADDR_ANY; // II.B.6. Try to bind socket with port and other specifications int status = bind(socketDescriptor, // from socket() (struct sockaddr*)&socketInfo, sizeof(socketInfo) ); if (status < 0) { perror(THIS_PROGRAM_NAME); return(ERROR_FD); } // II.B.6. Set OS queue length: listen(socketDescriptor,5); // III. Finished: return(socketDescriptor); } int main (int argc, char* argv[] ) { // I. Application validity check: // II. Do server: int port = getPortNum(argc,argv); int listenFd = getServerFileDescriptor(port); int status = EXIT_FAILURE; if (listenFd >= 0) { doServer(listenFd); close(listenFd); status = EXIT_SUCCESS; } // III. Finished: return(status); }
Implementing doServer(int listenFd) (10 Points):
doServer() should have a loop in which it waits for a client to connect to listenFd. When a client does, it should:
malloc() enough memory for 2 integers
put the file descriptor from accept() in one of those spaces
put the value of threadCount in the other space, and increment threadCount
Make a detached thread to handle this new client. I called my function handleClient(), but you may call yours whatever. Pass the address of your malloc()-ed array.
The loop should then go back for another accept().
void* handleClient(void* vPtr) (10 Points):
(Or whatever you call your function that runs a thread for the client.)The thread id and the file descriptor are passed, but they come in as a void* pointer.
Use another pointer to cast back to int*
Save the file descriptor and thread number in local vars
free() the memory
Print the thread number and do a loop like this:
// II.B. Read command: char buffer[BUFFER_LEN]; char command; int fileNum; char text[BUFFER_LEN]; int shouldContinue = 1; while (shouldContinue) { text[0] = '\0'; read(fd,buffer,BUFFER_LEN); printf("Thread %d received: %s ",threadNum,buffer); sscanf(buffer,"%c %d \"%[^\"]\"",&command,&fileNum,text); // YOUR CODE HERE }
It read()s a line of text from the client into buffer[], and parses the line into a command character, fileNum integer, and quote-delineated text[] string. (The fileNum and text[] may or may not be given, depending upon the value of command.)
Then do the following operations based upon the value of command. Except for QUIT_CMD_CHAR I strongly recommend using a different function for each!
When the function ends just have it do:
printf("Thread %d quitting. ",threadNum); return(NULL);
command == DIR_CMD_CHAR (15 Points):
Open the current directory (named "."). If an error occurs then just send STD_ERROR_MSG back to the client.
Copy as many entries that will fit into a buffer of length BUFFER_LEN. Be sure to put a separating ' ' after each entry.
Close the directory.
command == READ_CMD_CHAR (15 Points):
Open the file with the number fileNum. Get the name from the number with:
char fileName[BUFFER_LEN]; snprintf(fileName,BUFFER_LEN,"%d%s",fileNum,FILENAME_EXTENSION);
read() up to BUFFER_LEN into a buffer. Put a '\0' character at the end of the buffer.
Send that buffer to the client.
Close the file.
If an error occurs during any of this, then send STD_ERROR_MSG back to the client.
command == WRITE_CMD_CHAR (15 Points):
The opposite of READ_CMD_CHAR. fileNum and text have the file number and text respectively from the client. Save that text to the named file. If there is an error send then send STD_ERROR_MSG back to the client. If you succeeded then send STD_OKAY_MSG back to the client.
command == DELETE_CMD_CHAR (10 Points):
fileNum tells the file number of the file to delete. Using the unlink() command. If there is an error send then send STD_ERROR_MSGback to the client. If you succeeded then send STD_OKAY_MSG back to the client.
command == CALC_CMD_CHAR (15 Points):
fork() a child. If fork() fails then send STD_ERROR_MSG back to the client.
For the child, do the following:
char fileName[BUFFER_LEN]; snprintf(fileName,BUFFER_LEN,"%d%s",fileNum,FILENAME_EXTENSION); int inFd = open(fileName,O_RDONLY,0); int outFd = open(OUTPUT_FILENAME,O_WRONLY|O_CREAT|O_TRUNC,0660); int errFd = open(ERROR_FILENAME, O_WRONLY|O_CREAT|O_TRUNC,0660); if ( (inFd < 0) || (outFd < 0) || (errFd < 0) ) { fprintf(stderr,"Could not open one or more files "); exit(EXIT_FAILURE); } close(0); dup(inFd); close(1); dup(outFd); close(2); dup(errFd);
Then run CALC_PROGNAME. If running that fails, then do:
fprintf(stderr,"Could not execl %s ",CALC_PROGNAME); exit(EXIT_FAILURE);
For the parent wait() for the child. If the child crashed or did not return EXIT_SUCCESS then send STD_ERROR_MSG back to the client.
If the child did not crash and returned EXIT_SUCCESS, then read() the first BUFFER_LEN chars from the file named OUTPUT_FILENAME. If there is any more space left in your buffer, then read() from the file named ERROR_FILENAME into the same buffer so that it has BUFFER_LEN chars maximum.
close() both files.
Send your buffer back to the client.
command == QUIT_CMD_CHAR (10 Points):
Just send STD_BYE_MSG back to the client and set shouldContinue to 0 to quit the loop.
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started