Question
Overview: Write server that is able to: Send a listing of the files in a directory to the client Write file give to it by
Overview: Write server that is able to:
Send a listing of the files in a directory to the client Write file give to it by the client Spellcheck a file the client gave it Delete a file Sample output: Client: Server: $ ./wordClient Machine name [localhost]? (I just press enter to use localhost as the default) Port number? 2000 What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 1 Sending "l" . ..
What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 2 Text? Hello Muddah, hello Faddah, Here I am at Camp Grenada. Camp is very ent ertaining, And they say we'll have some fun if it stops raining. I went hiking w ith Joe Spivey, He developed poison ivy. You remember Leonard Skinner, He got Pt omaine poisoning last night after dinner. File number (0-63)? 0 Sending "w 0 "Hello Muddah, hello Faddah, Here I am at Camp Grenada. Camp is ve ry entertaining, And they say we'll have some fun if it stops raining. I went hi king with Joe Spivey, He developed poison ivy. You remember Leonard Skinner, He got Ptomaine poisoning last night after dinner. "" Okay What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 1 Sending "l" . .. 0.txt
What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 3 File number (0-63)? 1 Sending "s 1" Error doing operation
What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 3 File number (0-63)? 0 Sending "s 0" Muddah Faddah Spivey
What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 1 Sending "l" . .. 0.txt
What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 4 File number (0-63)? 1 Sending "d 1" Error doing operation What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 1 Sending "l" . .. 0.txt
What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 4 File number (0-63)? 0 Sending "d 0" Okay What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 1 Sending "l" . ..
What would you like to do: (1) List files (2) Write file (3) Spell check a file (4) Delete a file (0) Quit Your choice? 0 Sending "q" Good bye!
(I ran the server in a subdirectory of where the client and server were) $ ../wordServer Port number to monopolize? 2000 Thread 0 starting.
Thread 0 received: l
Thread 0 received: w 0 "Hello Muddah, hello Faddah, Here I am at Camp Grenada. Camp is very entertaining, And they say we'll have some fun if it stops raining. I went hiking with Joe Spivey, He developed poison ivy. You remember Leonard Sk inner, He got Ptomaine poisoning last night after dinner. "
Thread 0 received: l
Thread 0 received: s 1
Thread 0 received: s 0
Thread 0 received: l
Thread 0 received: d 1
Thread 0 received: l
Thread 0 received: d 0
Thread 0 received: l
Thread 0 received: q Thread 0 quitting. (That thread has ended, but the server is still running) Assignment: Copy and paste the following 3 files: /*-------------------------------------------------------------------------* *--- ---* *--- wordClientServer.h ---* *--- ---* *--- This file declares C functions and constants common to both ---* *--- wordClient.c and wordServer.c. ---* *--- ---* *--- ---- ---- ---- ---- ---- ---- ---- ---- ---* *--- ---* *--- Version 1a Joseph Phillips ---* *--- ---* *-------------------------------------------------------------------------*/
//--- Header file inclusion ---//
#include
//--- Definition of constants: ---//
#define BUFFER_LEN 512
#define DIR_CMD_CHAR 'l'
#define WRITE_CMD_CHAR 'w'
#define SPELL_CMD_CHAR 's'
#define DELETE_CMD_CHAR 'd'
#define QUIT_CMD_CHAR 'q'
const int MIN_FILE_NUM = 0;
const int MAX_FILE_NUM = 63;
#define SPELLER_PROGNAME "/usr/bin/aspell"
#define SPELLER_PROG_OPTION "list"
#define ENDING_TEXT "xxxyyyzzz"
#define FILENAME_EXTENSION ".txt" /*-------------------------------------------------------------------------* *--- ---* *--- wordClient.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 1a Joseph Phillips ---* *--- ---* *-------------------------------------------------------------------------*/
// Compile with: // $ gcc wordClient.c -o wordClient
//--- Header file inclusion ---//
#include "wordClientServer.h"
//--- Definition of constants: ---//
#define DEFAULT_HOSTNAME "localhost"
//--- 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 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(" Text? "); 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 'socketFd', and prints returned text. // No return value. void communicateWithServer (int socketFd ) { // 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; int fileNum; int numBytes;
do { printf ("What would you like to do: " "(1) List files " "(2) Write file " "(3) Spell check a file " "(4) Delete a 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 \"%s\"", WRITE_CMD_CHAR,getFileNumber(),getText() ); break;
case 3 : snprintf(buffer,BUFFER_LEN,"%c %d",SPELL_CMD_CHAR,getFileNumber()); break;
case 4 : snprintf(buffer,BUFFER_LEN,"%c %d",DELETE_CMD_CHAR,getFileNumber()); break;
}
printf("Sending \"%s\" ",buffer); write(socketFd,buffer,strlen(buffer)+1); numBytes = read (socketFd,buffer,BUFFER_LEN);
if (numBytes > 0) buffer[numBytes] = '\0';
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 socketFd;
obtainUrlAndPort(BUFFER_LEN,url,&port); socketFd = attemptToConnectToServer(url,port);
if (socketFd < 0) exit(EXIT_FAILURE);
communicateWithServer(socketFd); close(socketFd); return(EXIT_SUCCESS); } /*-------------------------------------------------------------------------* *--- ---* *--- wordServer.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 1a Joseph Phillips ---* *--- ---* *-------------------------------------------------------------------------*/
// Compile with: // $ gcc wordServer.c -o wordServer -lpthread
//--- Header file inclusion ---//
#include "wordClientServer.h" #include
//--- 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 "wordServer"
#define OUTPUT_FILENAME "out.txt"
#define ERROR_FILENAME "err.txt"
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
// III. Finished: }
// 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 nd 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 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 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 == WRITE_CMD_CHAR (15 Points): This one should open the file numbered fileNum for output. Then it should write text from the client to that 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 == SPELL_CMD_CHAR (30 Points): This one spell checks a file numbered fileNum by:
Opening it in read-write mode. If opening the file fails then it sends STD_ERROR_MSG back to the client. Putting ENDING_TEXT (a purposeful misspelling) at the end of the file. We so we can look for it in the output of the spell-checker: when we see it we know we have seen all the output. If fileFd is the file-descriptor of your input-output file you can do with: long length = lseek(fileFd,0,SEEK_END); int status = write(fileFd,ENDING_TEXT,sizeof(ENDING_TEXT)-1); Opens a pipe for the spell-checking child process to communicate back to the parent. If opening the pipe fails then it sends STD_ERROR_MSG back to the client. fork()s a child process. The child process resets its position in the input-output file back to the beginning of the file. It then has its input re-directed from the file descriptor of the file, and its output re-directed to the output side of the pipe. If fileFd is the file-descriptor of your input-output file, and fromSpeller[] is your pipe, you mayo this with: lseek(inFileFd,0,SEEK_SET); dup2(inFileFd,STDIN_FILENO); dup2(fromSpeller[1],STDOUT_FILENO); Then, the child process executes program SPELLER_PROGNAME with command line argument SPELLER_PROG_OPTION. If the child process cannot execute the program, then it sends STD_ERROR_MSG and ENDING_TEXT back to the parent process using the pipe and does exit(EXIT_FAILURE). Meanwhile, in a loop the parent process continually read()s from the input end of the pipe. Whatever it read()s it should put into a buffer which it will send back to the client. When it finds ENDING_TEXT it knows the input has ended, and it can quit the loop. Look for one string inside of another with strstr(). After the read()ing loop the parent process should: wait() for the child process to end remove ENDING_TEXT from the input-output file ftruncate(fileFd,length); close() that file and the pipe 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_MSG back to the client. If you succeeded then send STD_OKAY_MSG 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