Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

What is wrong with this code? I cannot get the port to connect to the socket at all. When I enter a port number the

What is wrong with this code? I cannot get the port to connect to the socket at all. When I enter a port number the client does nothing.

Instructions:

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.

/*-------------------------------------------------------------------------* *--- ---* *--- 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() #include #include //For socket() #include //For sockaddr_in and htons() #include #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 "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;

extern void* handleClient(void* vPtr); extern int getServerFileDescriptor();

//--- Definition of functions: ---//

// PURPOSE: To run the server by 'accept()'-ing client requests from // 'listenFd' and doing them. void doServer (int listenFd) { /* Application validity check: */

/* Server clients: */ pthread_t threadId; pthread_attr_t threadAttr; int threadCount = 0; int *iPtr;

/* YOUR CODE HERE: */ while(1) { /* Accept connection to client: */ int conffd = accept(listenFd, NULL, NULL); /* Malloc memory for two integers: */ iPtr = (int *)calloc(2, sizeof(int)); /*Put file descriptor in the first space: */ iPtr[0] = listenFd; // or just listenFd not sure

/* put threadCount into the second space and increment: */ iPtr[1] = threadCount++;

/* Creates detached thread for handleClient and passes the address of iPtr */ pthread_attr_init(&threadAttr); pthread_attr_setdetachstate(&threadAttr, PTHREAD_CREATE_DETACHED); pthread_create(&threadId, &threadAttr, handleClient, (void*)iPtr);

pthread_join(threadId, NULL); pthread_attr_destroy(&threadAttr);

} } void* handleClient(void* vPtr) { /* Read command: */ char buffer[BUFFER_LEN]; char command; int fileNum; char text[BUFFER_LEN]; int shouldContinue = 1; int threadNum; int fd; /* Cast void* vPtr back to an int */ int *iPtr = (int *)vPtr; /* Assign file descriptor to a local value named 'fd'*/ fd = iPtr[0]; /* Assign thread number to local value named 'threadNum'*/ threadNum = iPtr[1]; free(iPtr); while (shouldContinue) { memset(buffer,'\0',BUFFER_LEN); memset(text ,'\0',BUFFER_LEN);

read(fd,buffer,BUFFER_LEN); printf("Thread %d received: %s ",threadNum,buffer); sscanf(buffer,"%c %d \"%[^\"]\"",&command,&fileNum,text);

/* YOUR CODE HERE: */ if(command == DIR_CMD_CHAR) { /* 1. Open the current directory (named "."). If an error occurs then just send STD_ERROR_MSG back to the client: */ DIR* dirPtr = opendir("."); struct dirent* entryPtr; /* If error occurs send STD_ERROR_MSG to client: */ if ((dirPtr = opendir (".")) == NULL) { { write(fd, STD_ERROR_MSG, sizeof(STD_ERROR_MSG)); //return(EXIT_FAILURE); } /* Read as many entries that will fit into BUFFER_LEN put as many entries into the buffer and send the buffer to client d_name=entryPtr into the bufffer using strcat_s, make sure buffer starts empty buffer[0]=' '; add new line char using stringcat " " make sure do not go over buffer lengh */ if (dirPtr) { while ((entryPtr = readdir(dirPtr)) != NULL) { buffer[0]='\0'; int i; int sizebuf = sizeof(buffer); for (i = 0; i < sizebuf; i++) { strcat(buffer,entryPtr->d_name); strcat(buffer," "); } } } /* 3. Close directory */ closedir(dirPtr); } else if (command == READ_CMD_CHAR) { /* 1. Open the file with the number fileNum. Get the name from the number with: */ fileNum = open(FILENAME_EXTENSION,O_RDONLY, 0); //Yeah, I think this is right, except with fileNum instead. // changed fd to fileNum /* If error occurs send STD_ERROR_MSG to client: */ if(fileNum < 0) { write(fileNum, STD_ERROR_MSG, sizeof(STD_ERROR_MSG)); // it looks like this is correct exit(1); } /* 2. read() up to BUFFER_LEN into a buffer. Put a '\0' character at the end of the buffer: */ char fileName[BUFFER_LEN]; snprintf(fileName,BUFFER_LEN,"%d%s",fileNum,FILENAME_EXTENSION); fileNum = read(fileNum, fileName, BUFFER_LEN-1); fileName[fileNum] = '\0'; printf("Sending: %s", fileName); write(fileNum, fileName, fileNum); /* Close file: */ close(fileNum); }

else if (command == WRITE_CMD_CHAR) { /* 1. Open the file with the number fileNum in write mode: */ fileNum = open(FILENAME_EXTENSION,O_WRONLY|O_CREAT|O_TRUNC,0660); /* If error occurs send STD_ERROR_MSG to client: */ if(fileNum < 0) { write(fileNum, STD_ERROR_MSG, sizeof(STD_ERROR_MSG)); } if (fileNum >0) { //fileNum and text have the file number and text respectively from the client. //Save that text to the named file. char fileName[BUFFER_LEN]; write (fileNum,fileName, sizeof(fileName)); //If you succeeded then send STD_OKAY_MSG back to the client write(fileNum, STD_OKAY_MSG, sizeof(STD_OKAY_MSG)); } /* Close file: */ close(fileNum); }

else if (command == CALC_CMD_CHAR) { /* Create new fork named calcPid: */ int calcPid = fork(); /* If fork() failed: */ if(calcPid < 0) { write(fd, STD_ERROR_MSG, sizeof(STD_ERROR_MSG)); } /* Child writes files: */ else if(calcPid == 0) { 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); /* Run CALC_PROGNAME, if fails generate output: */ execl(CALC_PROGNAME,CALC_PROGNAME,fileName,NULL); fprintf(stderr,"Could not execl %s ",CALC_PROGNAME); exit(EXIT_FAILURE);

} /* Parent reads files: */ else { /* Wait and check status of child process, if it crashes return STD_ERROR_MSG: */ int status; pid_t return_pid = waitpid(calcPid, &status, WNOHANG); if (return_pid == -1) write(fd, STD_ERROR_MSG, sizeof(STD_ERROR_MSG)); /* If child process EXIT_SUCCESS, then run parent process: */ if (return_pid == calcPid) { char fileName[BUFFER_LEN]; snprintf(fileName,BUFFER_LEN,"%d%s",fileNum,FILENAME_EXTENSION); int numBytes = 0; int outFd = open(OUTPUT_FILENAME,O_RDONLY|O_CREAT,0660); int errFd = open(ERROR_FILENAME, O_RDONLY|O_CREAT,0660);

if ( (outFd < 0) || (errFd < 0) ) { fprintf(stderr,"Could not open one or more files "); exit(EXIT_FAILURE); } while ( (numBytes = read(outFd, fileName, BUFFER_LEN)) > 0) read(errFd, &fileName[numBytes], BUFFER_LEN); close(0); dup(outFd); close(1); dup(errFd); } } } else if (command == DELETE_CMD_CHAR) { if (fileNum > 0) { fileNum = unlink(buffer); write(fd, STD_OKAY_MSG, sizeof(STD_OKAY_MSG)); } else write(fd, STD_ERROR_MSG, sizeof(STD_ERROR_MSG)); } else if (command == QUIT_CMD_CHAR) { /* Send STD_BYE_MSG back to the client and set shouldContinue to 0 to quit the loop: */ write(fd, STD_BYE_MSG, sizeof(STD_BYE_MSG)); shouldContinue = 0; } } } printf("Thread %d quitting. ",threadNum); return(NULL); }

// 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); }

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Students also viewed these Databases questions